外接设备读取u盘里面的图片并显示

时间:2022-08-14 08:46:01

做了很久的相册功能,真不容易,边做边学,而且还没有做完,因为想从ec换成as,所以在as downloads的时候我就先把过程中的一些问题和知识点先记下来。

外接设备读取u盘里面的图片并显示

首先,项目中使用了,aidl,官方解释是:安卓接口定义语言,

具体参考:https://blog.csdn.net/u011974987/article/details/51243539;

然后因为是我们老大给我的源码,但是,很乱,但是这个aidl是必须有的,于是我就考了她的。上代码

-----------------------------------------------FilePhotoInfo.java 的源码---------------------------------------------------

package com.goldhonor.aidl;

import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

public class FilePhotoInfo implements Parcelable {

String path = "";
long size = 0;// 单位B

public FilePhotoInfo(String path, long size) {
// TODO Auto-generated constructor stub
this.path = path;
this.size = size;
}

public String getPath() {
return path;
}

public long getSize() {
return size;
}

@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeString(path);
dest.writeLong(size);
}

public static final Parcelable.Creator<FilePhotoInfo> CREATOR = new Parcelable.Creator<FilePhotoInfo>() {
public FilePhotoInfo createFromParcel(Parcel in) {
String path = in.readString();
long size = in.readLong();
return new FilePhotoInfo(path, size);
}

public FilePhotoInfo[] newArray(int size) {
return new FilePhotoInfo[size];
}
};

}

------------------------------------------------FilePhotoInfo.aidl 的源码---------------------------------------------------

外接设备读取u盘里面的图片并显示

------------------------------------------------MediaFileService.aidl的源码---------------------------------------------------

必须有个这个文件,它可以生成一个.java文件在gen文件夹下面,但是你如果只做相册可以忽略其他两个哈

外接设备读取u盘里面的图片并显示

------------------------------------------------PhotoInfo源码------------------------------------------------

public class PhotoInfo {


String path;
boolean isDel = false;
String parentFolderName = "";// 文件夹名字
String name = "";// 文件名
String noHzName = "";// 没有后缀的名字


public PhotoInfo(String path) {
this.path = path;
//在new 一个PhotoInfo对象的时候就可以调用初始化的方法
initPathInfo();
}


// 在实体类里面直接给照片的一些属性初始化(其实我感觉没什么用啊,我有不需要显示)
private void initPathInfo() {
try {


// 传一个正则表达式,如果是特殊字符如:* . ? + $ ^ [ ] ( ) { } | \
// 就需要用\来转义,例如:*,传的时候需要写成"\*"。
String str[] = path.split("/");
//Log.d("ppx", "文件路径:-------------------"+path);
// 例如: /storage/usb0/图片/IMG_20151022_064305.jpg 4
// 或者: /storage/usb0/图片/u=3500982766,321917490&fm=21&gp=0.jpg 4
// 或者: /storage/usb0/5b6f730febb78514b2417a359530117b.jpg 3
// 像最后一种的就是在根目录下面了
if (str != null && str.length > 2) {// /storage/usb0/    根目录的长度为2
//给装图片的文件夹名赋值
if(str.length==3){//最短的这种
parentFolderName = "usb0";
}else{
parentFolderName = str[str.length-2];
//给文件名赋值
name = str[str.length-1];
//给没有后缀的名字
noHzName = name.substring(0, name.lastIndexOf("."));//包前不包后
//Log.d("ppx", "文件名:-------------"+name);
//Log.d("ppx", "文件夹名:-------------"+parentFolderName);
}
}

} catch (Exception e) {
e.printStackTrace();
Log.e("error", "问价路径:-----------"+this.getPath());
}
}


public String getName() {
return name;
}


public String getNoHzName() {
return noHzName;
}


public String getParentFolderName() {
return parentFolderName;
}


public void setDel(boolean isDelSel) {
this.isDel = isDelSel;
}


public boolean isDel() {
return isDel;
}


public String getPath() {
return path;
}
}

------------------------------------------------GradViewBaseAdapter.java源码------------------------------------------------

package com.goldhonor.ex50photo;


import java.util.List;


import com.example.ex50photo.R;
import com.goldhonor.ex50photo.PhotoInfo;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;


/**
 * GridView 采用水平堆叠的方式显示数据。对于占驻较多控件的每个项目(如照片库),当你需要为其显示丰富的视觉信息时,该控件很常用。
 * 
 * @author ppx
 *
 */
public class GridViewBaseAdapter extends BaseAdapter {
// 数据源
private List<PhotoInfo> list;
// 布局装载器对象
private LayoutInflater mInflater;
ViewHolder viewholder;


// 构造方法联系数据源(list)和适配器(GridViewBaseAdapter)
public GridViewBaseAdapter(Context context, List<PhotoInfo> list) {
this.list = list;
this.mInflater = LayoutInflater.from(context);
}


// 获取数据源的数量
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}


// 根据指定索引获取对应的数据项
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return list.get(arg0);
}


// 根据指定索引获取对象数据项的下标
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}


// 返回所以控件的数据
@Override
public View getView(int position, View convertview, ViewGroup arg2) {
if (convertview == null) {
viewholder = new ViewHolder();
// xml转成view,格局样式为null
convertview = mInflater.inflate(R.layout.gridview_item, null);
// 给控件对象(ViewHolder的属性)赋值(其实是先找到控件对象)
viewholder.gridview_item_imageview_image = (ImageView) convertview
.findViewById(R.id.gridview_item_imageview_image);
convertview.setTag(viewholder);
} else {
viewholder = (ViewHolder) convertview.getTag();
}
// 取出bean
PhotoInfo bean = list.get(position);
viewholder.gridview_item_imageview_image.setImageBitmap(getThumbNail(
bean.getPath(), 200, 200));
return convertview;
}


/**
* 解决oom,使用ThunbNail

* @author ppx
*
*/
public Bitmap getThumbNail(String pathName, int destinyHeight,
int destinyWidth) {
Bitmap bitmap = null;
BitmapFactory.Options opts = new BitmapFactory.Options();
// 暂不加载图片
opts.inJustDecodeBounds = true;
// 第一次获取的bitmap是个空对象
bitmap = BitmapFactory.decodeFile(pathName, opts);
// 获取宽高
int width = opts.outWidth;
int height = opts.outHeight;
// 计算压缩比
opts.inSampleSize = Math.min(width / destinyWidth, height
/ destinyHeight) <= 0 ? 1 : Math.min(width / destinyWidth,
height / destinyHeight);
// 上述方法可以得到满意的图片,但是Factory在decode的时候,仍然没有节约内存,使用下面三个属性,真正节约手机有限的内存:(据说可以省内存,试一下?)
opts.inPreferredConfig = Bitmap.Config.RGB_565;// 默认为ARGB_8888.
// 以下两个字段需一起使用:
opts.inPurgeable = true;// 产生的位图将得到像素空间,如果系统gc,那么将被清空。当像素再次被访问,如果Bitmap已经decode,那么将被自动重新解码
opts.inInputShareable = true;// 位图可以共享一个参考输入数据(inputstream、阵列等)
// 开始加载
opts.inJustDecodeBounds = false;
// 第二次获取bitmap
bitmap = BitmapFactory.decodeFile(pathName, opts);
// 第三次获取压缩图
bitmap = ThumbnailUtils.extractThumbnail(bitmap, destinyWidth,
destinyHeight, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
return bitmap;
}


class ViewHolder {
public ImageView gridview_item_imageview_image;
}
}

------------------------------------------------MainActivity.java源码------------------------------------------------

package com.goldhonor.ex50photo;


import java.util.ArrayList;
import java.util.List;


import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.widget.GridView;
import android.widget.ImageView;


import com.example.ex50photo.R;
import com.goldhonor.aidl.FilePhotoInfo;
import com.goldhonor.aidl.MediaFileService;


public class MainActivity extends Activity {
MediaFileService pointerObject;
GridView gridview;
private ServiceConnection conn;
GridView activity_main_gridview_image;
ImageView gridview_item_imageview_image;
ImageView big_imageview;


// 相册的扫描状态
int photoScanStatus = -1;
// 几种action
String ACTION_PHOTO_CHANGED = "goldhonor.action.mediafile.photochanged";
String ACTION_MEDIA_STATUS = "goldhonor.action.mediastatus";
String ACTION_MEDIA_EJECT = "goldhonor.action.mediaeject";


@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("ppx", "1onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取gridview对象
activity_main_gridview_image = (GridView) findViewById(R.id.activity_main_gridview_image);
gridview_item_imageview_image = (ImageView) findViewById(R.id.gridview_item_imageview_image);
big_imageview = (ImageView) findViewById(R.id.big_imageview);
// 1、服务通信
conn = new ServiceConnection() {


@Override
public void onServiceDisconnected(ComponentName name) {
}


@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定的这个服务是赖姐自己写的服务,然后编译在真机上面的,所以只能在编译了服务的真机上面才能做绑定服务的操作
Log.d("ppx", "3连接上服务---------------");
// 服务连接上的时候就获取指针对象
pointerObject = MediaFileService.Stub.asInterface(service);
// 以前连接服务的时候就是为了调用服务里面的方法,现在连接服务是为了获取这个指针对象???
try {
photoScanStatus = pointerObject.getPhotoScanStatus();
Log.d("ppx", "4相册的扫描状态:----------" + photoScanStatus);
} catch (RemoteException e) {
Log.e("ppx", "获取相册扫描状态出错");
e.printStackTrace();
}
// 我怎么知道在扫描的相册的状态码是多少,??通信协议上面也没有写的
// 答:在赖姐写的服务上面有说明,0:未扫描;1:第一次遍历扫描;2:第二次扫描获取详情;3:扫描完成
// 之前一直是0,是因为服务没有运行起来,通过查看服务的log日志可知,还有就是U盘根本没有扫描到,因为机器线断了orz
if (photoScanStatus == 2) {// 在操作相册
showImage();
}
}
};
// 2、绑定服务
bindService();
}


public void bindService() {
Log.d("ppx", "2正在绑定服务-------------");
Intent service = new Intent("com.goldhonor.mediaservice");// 这个action还在哪里配置了的吗????
service.setPackage("com.goldhonor.mediaprovider");
// 开启服务
startServiceAsUser(service, UserHandle.OWNER);// 作为系统用户的开启服务
bindServiceAsUser(service, conn, Context.BIND_AUTO_CREATE,
UserHandle.OWNER);
// 意图过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_PHOTO_CHANGED);
filter.addAction(ACTION_MEDIA_STATUS);
filter.addAction(ACTION_MEDIA_EJECT);
// 调用注册广播接收器的方法
registerReceiver(receiver, filter);
}


// 解绑服务
public void unbindService() {
unbindService(conn);
// 解绑的时候也需要把广播接收器反注册了
unregisterReceiver(receiver);
}


// 注册一个广播接收器
private BroadcastReceiver receiver = new BroadcastReceiver() {


@Override
public void onReceive(Context context, Intent intent) {
// 判断当前的操作
if (intent != null
&& intent.getAction().equals(ACTION_PHOTO_CHANGED)) {
Log.d("ppx", "相册变动----showImage-----------------");
// 相册变动,当U盘插入的时候扫描并显示图片
showImage();
} else if (intent != null
&& intent.getAction().equals(ACTION_MEDIA_STATUS)) {
//
} else if (intent != null
&& intent.getAction().equals(ACTION_MEDIA_EJECT)) {
// 拨叉情况
} else {
Log.d("ppx", "没有找到对应的action");
}
}
};


List<PhotoInfo> list;


// 查询到相册信息的时候
public void showImage() {
try {
List<FilePhotoInfo> filelist = pointerObject.getPhotoItems();
list = new ArrayList<PhotoInfo>();
list.clear();
for (FilePhotoInfo flist : filelist) {
PhotoInfo photoInfo = new PhotoInfo(flist.getPath());
Log.d("ppx", "图片的路径-------------" + photoInfo.path);
list.add(photoInfo);
}
//设置adapter
activity_main_gridview_image.setAdapter(new GridViewBaseAdapter(
this, list));
} catch (RemoteException e) {
e.printStackTrace();
}
}


// 单击imageView的时候,放大单击的图片,隐藏其他的图片
public void onClick(View view) {
Log.d("ppx", "进入单击事件-------------");
// // 获取当前单击的ImageView的图片
// Drawable bigImageView = gridview_item_imageview_image.getBackground();
// // 设置为大图
// big_imageview.setBackground(bigImageView);
// //将其他小图隐藏
// gridview_item_imageview_image.setVisibility(View.INVISIBLE);
// //不知道是应该隐藏imageView还是GridView,试一下两个都隐藏了
// gridview.setVisibility(View.INVISIBLE);
}


public void onDestroy() {
unbindService();
}

}

------------------------------------------------另外还有layout文件------------------------------------------------

------------------------------------------------activity-main.xml------------------------------------------------

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000"
    android:orientation="horizontal" >


    <ListView
        android:id="@+id/lv_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />
    <!--
      今天又吃了个亏,明明线性布局的LinearLayout,我知道是从上到下排列的,但是!!!!!!我却没有注意我的
第一个控件ListView的宽高全特么是适应父窗体?exm??喵了个咪
android:visibility="gone"   将listview完全隐藏,不保存空间位置,但是invisible就是隐藏元素保留空间位置
    -->


    <GridView
        android:id="@+id/activity_main_gridview_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnWidth="200dp"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth" />


</LinearLayout>
<!-- 
columnWidth:要显示的图片的宽度
stretchMode:图片的排列模式
 -->

------------------------------------------------gridview-item.xml------------------------------------------------

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >


   <ImageView
        android:id="@+id/gridview_item_imageview_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="20dp"
        android:onClick="onClick"
        />
    
    <TextView
        android:id="@+id/tv_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dp" 
        android:layout_gravity="center"
        android:textColor="#FFF"
        />
    
    <ImageView 
        android:id="@+id/big_imageview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</LinearLayout>

------------------------------------------------AndroidManifest.xml------------------------------------------------

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.ex50photo"
    android:versionCode="1"
    android:versionName="1.0" >


    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="16" />


    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.goldhonor.ex50photo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="goldhonor.action.mediafile.photochanged" />


                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="goldhonor.action.mediastatus" />


                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="goldhonor.action.mediaeject" />


                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <service android:name="com.goldhonor.ex50photo.MyService" />
    </application>


</manifest>

第一次写那么长的原文,代码全部,全部,全部都在这里面了,拿过去直接运行没问题的,如果有问题可以留言,我工作日都会看哒,里面注释也很仔细了,还有,如果有认真看了的同学应该发现了,我的相册还没有做点击放大,再次点击还原的效果,一直还在学习,进度很慢,希望大家能多评论我,努力学习啊巴拉巴拉,好了就到这里 吧,我去研究了。。

突然想起,你们拿过去应该是不能运行的,因为你们那边没有那个服务阿,,阿西吧,所以大家就主要看逻辑代码吧,比如怎么显示图的,怎么解决的oom,都是我自己去百度了很多测试了很多然后慢慢写的。