【攻克Android (12)】BaseAdapter 自定义适配器

[b][size=large]本文围绕以下三个部分展开: [/size][/b]

[b][size=large]一、BaseAdapter 自定义适配器[/size][/b]
[b][size=large]二、一个案例[/size][/b]

[b][size=large]附 代码补充[/size][/b]


[b][size=large]一、BaseAdapter 自定义适配器[/size][/b]

[size=medium]它是 Android 应用程序中经常用到的基础适配器,它的主要用途是将一组数据传到 ListView、Spinner、Gallery 及 GridView 等 UI 显示组件。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4812/bcee14a6-b303-38a7-98a0-83901ee16c7c.png[/img][/align]


[b][size=large]二、一个案例[/size][/b]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4815/fa3dc52d-365e-3101-937a-bc61d8cd0ccb.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4818/0889d15d-ab1a-3241-b029-ef1bf9601ca6.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4820/cb19f0fa-6c0b-3b68-a17f-9916fdacd41d.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4822/8a9563db-51ab-35de-b2f9-4c00025b218f.png[/img][/align]

[size=medium]案例说明:[/size]

[size=medium]读取手机的SD卡中的目录和文件(全部读出/过滤不允许读的文件和隐藏文件),并显示成上图的样子。[/size]

[size=medium]目录:显示出目录(文件夹)的图标、目录名、包含几个文件。文件:显示出文件的图标、文件名、文件大小。[/size]

[size=medium]点击右边的按钮,出现弹出菜单,里面有“复制”和“删除”两项。分别点击,会弹出提示信息。[/size]


[size=medium][b]1. 在 styles.xml(v21) 中设置标题栏颜色。[/b][/size]

 <item name="android:colorPrimaryDark">@android:color/holo_blue_dark</item>  
<item name="android:colorPrimary">@android:color/holo_blue_light</item>
<item name="android:navigationBarColor">@android:color/transparent</item>


[size=medium][b]2. 在功能清单中授予读取SD卡的权限,这样,本APP才可以读取到手机SD卡。[/b][/size]

 <!-- 授予此App读取SD卡的权限  
(注意大小写,不能写为:ANDROID.PERMISSION.READ_EXTERNAL_STORAGE)-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


[size=medium][b]3. 启动后,会先启动MainAcitivity,然后加载主活动的布局文件(主界面)。[/b][/size]

 protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);
// 加载主活动的布局文件
setContentView(R.layout.activity_main);
}


[size=medium]因此要写主界面 activity_main.xml :列表框(ListView)。[/size]

 <!-- 主活动的布局文件:列表框-->  
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>


[size=medium]因为ListView里面也有布局文件,用来放图片、文件/目录名等控件,因此接下来写里面的布局文件。该布局文件中,有一些字符串,因此先在 strings.xml 中写需要的字符串。[/size]

 <string name="filename">文件名</string>  
<string name="summary">文件/目录 概要信息</string>
<string name="copy">复制</string>
<string name="delete">删除</string>
<string name="sd_card_error">读取SD卡出错</string>


[size=medium]然后再创建里面的布局文件:file_item.xml ,然后写布局。[/size]

 <ImageView  
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_file"
android:contentDescription="@string/filename"/>

<ImageButton
android:id="@+id/btnOperator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/ic_more_vert_grey600_16dp"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Small"/>

<TextView
android:id="@+id/tvFilename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/imageView"
android:layout_marginLeft="18dp"
android:layout_toRightOf="@id/imageView"
android:text="@string/filename"
android:textSize="16sp"/>

<TextView
android:id="@+id/tvSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/tvFilename"
android:layout_alignParentBottom="true"
android:layout_alignStart="@id/tvFilename"
android:text="@string/summary"
android:textSize="12sp"/>


[size=medium][b]4. 判断SD卡状态,并读取SD卡内容。[/b][/size]

[size=medium](1)判断SD卡状态是否有效。如果有效,弹出提示“SD card is ok”;否则,弹出提示:“读取SD卡出错”。[/size]

		 // 获得当前设备的SD卡的状态:Environment.getExternalStorageState()  
// SD卡正在使用:Environment.MEDIA_MOUNTED
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this,"SD card is ok",Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, getString(R.string.sd_card_error), Toast.LENGTH_SHORT).show();
}


[size=medium]然后在手机上部署,显示以下效果,说明SD卡正在使用。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4826/cc28f7ab-d2e0-3f87-8fc0-2a22327e0f5d.png[/img][/align]

[size=medium](2)获得SD卡的根目录下的目录/文件列表。(此处获得的,是所有的目录和文件列表)[/size]

 public class MainActivity extends Activity {  
private ListView listView;
private File[] files;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);

// 获得当前设备的SD卡的状态:Environment.getExternalStorageState()
// SD卡正在使用:Environment.MEDIA_MOUNTED
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this,"SD card is ok",Toast.LENGTH_SHORT).show();
// 获得SD卡的根目录下的目录/文件
files = Environment.getExternalStorageDirectory().listFiles();

} else {
Toast.makeText(this, getString(R.string.sd_card_error), Toast.LENGTH_SHORT).show();
}
}


[size=medium](3)先初始化根路径,然后通过创建文件过滤器,过滤不允许读的文件和隐藏文件,接着获得SD卡根目录下面允许读的文件/目录列表。[/size]

 public class MainActivity extends Activity {  
private ListView listView;
private File[] files;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);

// 获得当前设备的SD卡的状态:Environment.getExternalStorageState()
// SD卡正在使用:Environment.MEDIA_MOUNTED
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// 初始化根路径(/storage/emulated/0)
File sdPath = Environment.getExternalStorageDirectory();
// 创建文件过滤器(过滤不允许读的文件/隐藏文件)
SdFileFilter sdFileFilter = new SdFileFilter();

// 获得SD卡的根目录下的目录/文件(文件列表)
files = sdPath.listFiles(sdFileFilter);

} else {
Toast.makeText(this, getString(R.string.sd_card_error), Toast.LENGTH_SHORT).show();
}
}


[size=medium]其中,用到了SD卡文件过滤器 SdFileFilter ,因此需创建此文件过滤器。[/size]

 package com.xiangdong.baseadapter;  

import java.io.File;
import java.io.FileFilter;

/**
* SD Card文件过滤器
* Created by Xiangdong on 2015/6/4.
*/
public class SdFileFilter implements FileFilter {

/**
* 不允许读的文件/隐藏文件 不显示
*/
@Override
public boolean accept(File pathname) {
if (pathname.isHidden() || !pathname.canRead()) {
return false;
}
return true;
}
}


[size=medium]注意:以上只是获得了文件/目录列表,并放入了数组中,但并未进行填充。[/size]

[size=medium][b]5. 创建文件适配器 FileAdapter 类,继承自 BaseAdapter。然后将数据与该适配器关联,接着将布局与适配器关联。[/b][/size]

 public class MainActivity extends Activity {  
private ListView listView;
private File[] files;
private FileAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File sdPath = Environment.getExternalStorageDirectory();
SdFileFilter sdFileFilter = new SdFileFilter();
files = sdPath.listFiles(sdFileFilter);

// 数据(文件列表)显示的方式:数据与适配器关联
adapter = new FileAdapter(this, files);
// 布局与适配器关联
listView.setAdapter(adapter);
} else {
Toast.makeText(this, getString(R.string.sd_card_error), Toast.LENGTH_SHORT).show();
}
}


[size=medium][b]6. 进入 FileAdapter 类,写该文件适配器。[/b][/size]

[size=medium](1)创建构造方法。[/size]

 /** 
* 自定义文件适配器类
* Created by Xiangdong on 2015/6/4.
*/
public class FileAdapter extends BaseAdapter {
// 上下文
private Context context;
// 数据(文件列表)
private File[] files;
// 布局填充器(加载布局文件)
private LayoutInflater inflater;

/**
* 4.1 构造方法
*
* @param context 上下文
* @param files 数据(文件列表)
*/
public FileAdapter(Context context, File[] files) {
this.context = context;
this.files = files;
inflater = LayoutInflater.from(context);
}
}


[size=medium](2)继承了 BaseAdapter 类,要重写(override)四个方法。 [/size]

[size=medium](2.1)重写 getCount() 方法。[/size]

 /** 
* 获得数据的总数
*
* @return 文件列表的长度
*/
@Override
public int getCount() {
return files.length;
}


[size=medium](2.2)重写 getItem(int position) 方法。[/size]

 /** 
* 获得特定位置的数据
*
* @param position 位置
* @return 特定位置的数据
*/
@Override
public Object getItem(int position) {
return files[position];
}


[size=medium](2.3)重写 getItemId(int position) 方法。[/size]

 /** 
* 获得特定位置数据的 id (使用SQLite的场景)
*
* @param position 位置
* @return 特定位置数据的 id
*/
@Override
public long getItemId(int position) {
// 位置是列表项在 ListView 中的索引
// 在排序规则改变时,位置的数值会改变
// id 是数据的唯一标识,不会改变
return 0;
}


[size=medium](2.4)重写 getView(int position, View convertView, ViewGroup parent) 方法。[/size]

 /** 
* (2.4) 获得特定位置加载了数据的视图(列表项)
*
* @param position 位置
* @param convertView 可重用的视图
* @param parent 父元素
* @return 列表项
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
ImageButtonListener listener = null;

if (convertView == null) {
// 若无【可重用的视图】,才实例化 xml 文件创建视图
// inflate 方法执行的次数为屏幕上能显示的列表项的最大值
convertView = inflater.inflate(R.layout.file_item, parent, false);

// 创建【结构持有者】,获得视图中的各个控件
holder = new ViewHolder();
holder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
holder.btnOperator = (ImageButton) convertView.findViewById(R.id.btnOperator);
holder.tvFilename = (TextView) convertView.findViewById(R.id.tvFilename);
holder.tvSummary = (TextView) convertView.findViewById(R.id.tvSummary);

// 创建按钮的监听器
listener = new ImageButtonListener();
// 注册监听器
holder.btnOperator.setOnClickListener(listener);
// 将监听器存储【绑定】到按钮中
holder.btnOperator.setTag(listener);

// 将【结构持有者】存储到视图中
convertView.setTag(holder);
} else {
// 有【可重用的视图】,则从中取出它的【结构持有者】
holder = (ViewHolder) convertView.getTag();

// 获得按钮的监听器
listener = (ImageButtonListener) holder.btnOperator.getTag();
}

// 在【结构持有者】中加载 position 位置的数据
// 获得的是根据位置取出的一个,返回的是一个文件
File file = files[position];

// 进行填充
holder.tvFilename.setText(file.getName());
holder.tvSummary.setText(file.isFile()
? String.format(context.getString(R.string.fileSize), file.length())
: String.format(context.getString(R.string.contents), file.listFiles().length));
holder.imageView.setImageResource(file.isFile()
? R.drawable.ic_file
: R.drawable.ic_folder);

// 修改监听器的监听的数据
listener.setData(file);

// 返回视图
return convertView;
}


[size=medium]注:上面在填充的时候,需要填充 “文件大小:%,d 字节”和“目录: %d 个字节”,因此把这两个字符串也写进 strings.xml 中。[/size]

 <string name="fileSize">文件大小:%,d 字节</string>  
<string name="contents">目录: %d 个文件</string>


[size=medium](2.4.1)列表项的【结构持有者】:ViewHolder 。[/size]

 /** 
* (2.4.1)列表项的 结构持有者
* 存储 file_item.xml 文件中的控件结构
* 直接通过字段访问,为提高性能
*/
private static class ViewHolder {
ImageView imageView;
ImageButton btnOperator;
TextView tvFilename;
TextView tvSummary;
}


[size=medium](2.4.2)自定义的 ImageButton 点击监听器:ImageButtonListener 。[/size]

 /** 
* (2.4.2)FileAdapter 的内部类
* 自定义的 ImageButton 点击监听器
*/
private class ImageButtonListener implements View.OnClickListener {
// 点击时获得的数据(文件/目录)
private File data;

public void setData(File data) {
this.data = data;
}

@Override
public void onClick(View v) {
// 创建弹出菜单
PopupMenu menu = new PopupMenu(context, v);
// 加载菜单文件
menu.inflate(R.menu.popup);
// 注册菜单选项事件
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
String text = "";
switch (item.getItemId()) {
case R.id.action_copy:
// copyFile();
text = context.getString(R.string.copy);
break;
case R.id.action_delete:
// deleteFile();
text = context.getString(R.string.delete);
}
text += ": " + data.getName();
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
return true;
}
});
// 显示菜单
menu.show();
}
}


[size=medium]注:在 ImageButtonListener 中,这儿实现的是,在点击“复制”或“删除”后,弹出提示信息。也可以,通过调用 copyFile() 或者 deleteFile() 方法,然后通过流来实现文件/目录的复制和删除。[/size]

[size=medium]上面用到了弹出菜单 PopupMenu ,因此要创建弹出菜单: popup.xml。[/size]

 <?xml version="1.0" encoding="utf-8"?>  
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/action_copy"
android:title="@string/copy"/>

<item
android:id="@+id/action_delete"
android:title="@string/delete"/>
</menu>



[b][size=large]附 代码补充[/size][/b]

[size=medium]项目目录结构如下:[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/4832/a5fb1d3d-3ba4-3265-a989-ccfde3c507e1.png[/img][/align]

[size=medium](1)styles.xml(v21) [/size]

 <?xml version="1.0" encoding="utf-8"?>  
<resources>
<style name="AppTheme" parent="android:Theme.Material.Light">
<item name="android:colorPrimaryDark">@android:color/holo_blue_dark</item>
<item name="android:colorPrimary">@android:color/holo_blue_light</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>


[size=medium](2)AndroidManifest.xml[/size]

 <?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiangdong.baseadapter" >

<!--1. 授予此App读取SD卡的权限
(注意大小写,不能写为:ANDROID.PERMISSION.READ_EXTERNAL_STORAGE)-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


[size=medium](3)strings.xml[/size]

 <resources>  
<string name="app_name">BaseAdapter</string>

<string name="action_settings">Settings</string>

<string name="filename">文件名</string>
<string name="summary">文件/目录 概要信息</string>
<string name="copy">复制</string>
<string name="delete">删除</string>
<string name="sd_card_error">读取SD卡出错</string>

<string name="fileSize">文件大小:%,d 字节</string>
<string name="contents">目录: %d 个文件</string>
</resources>


[size=medium](4)activity_main.xml[/size]

 <RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<!-- 2.主活动的布局文件:列表框-->
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</RelativeLayout>


[size=medium](5)file_item.xml[/size]

 <?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="72dp"
android:padding="@dimen/activity_horizontal_margin"
android:descendantFocusability="blocksDescendants">

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_file"
android:contentDescription="@string/filename"/>

<ImageButton
android:id="@+id/btnOperator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/ic_more_vert_grey600_16dp"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Small"/>

<TextView
android:id="@+id/tvFilename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/imageView"
android:layout_marginLeft="18dp"
android:layout_toRightOf="@id/imageView"
android:text="@string/filename"
android:textSize="16sp"/>

<TextView
android:id="@+id/tvSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/tvFilename"
android:layout_alignParentBottom="true"
android:layout_alignStart="@id/tvFilename"
android:text="@string/summary"
android:textSize="12sp"/>

</RelativeLayout>


[size=medium](6)MainActivity[/size]

 package com.xiangdong.baseadapter;  

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.Toast;

import java.io.File;


public class MainActivity extends Activity {
private ListView listView;
private File[] files;
private FileAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 2.加载主活动的布局文件
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);

// 3.获得当前设备的SD卡的状态:Environment.getExternalStorageState()
// SD卡正在使用:Environment.MEDIA_MOUNTED
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//Toast.makeText(this,"SD card is ok",Toast.LENGTH_SHORT).show();
// 获得SD卡的根目录下的目录/文件
//files = Environment.getExternalStorageDirectory().listFiles();

// 3.1 初始化根路径(/storage/emulated/0)
File sdPath = Environment.getExternalStorageDirectory();
// 3.2 创建文件过滤器(过滤不允许读的文件/隐藏文件)
SdFileFilter sdFileFilter = new SdFileFilter();

// 3.3获得SD卡的根目录下的目录/文件(文件列表)
files = sdPath.listFiles(sdFileFilter);

// 4.数据(文件列表)显示的方式:数据与适配器关联
adapter = new FileAdapter(this, files);
// 布局与适配器关联
listView.setAdapter(adapter);
} else {
Toast.makeText(this, getString(R.string.sd_card_error), Toast.LENGTH_SHORT).show();
}
}

//-------------------------------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}


[size=medium](7)SdFileFilter[/size]

 package com.xiangdong.baseadapter;  

import java.io.File;
import java.io.FileFilter;

/**
* SD Card文件过滤器
* Created by Xiangdong on 2015/6/4.
*/
public class SdFileFilter implements FileFilter {

/**
* 不允许读的文件/隐藏文件 不显示
*/
@Override
public boolean accept(File pathname) {
if (pathname.isHidden() || !pathname.canRead()) {
return false;
}
return true;
}
}


[size=medium](8)FileAdapter [/size]

 package com.xiangdong.baseadapter;  

import android.content.Context;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;

/**
* 自定义文件适配器类
* Created by Xiangdong on 2015/6/4.
*/
public class FileAdapter extends BaseAdapter {
// 上下文
private Context context;
// 数据(文件列表)
private File[] files;
// 布局填充器(加载布局文件)
private LayoutInflater inflater;

/**
* 4.1 构造方法
*
* @param context 上下文
* @param files 数据(文件列表)
*/
public FileAdapter(Context context, File[] files) {
this.context = context;
this.files = files;
inflater = LayoutInflater.from(context);
}

/**
* 4.2.1 获得数据的总数
*
* @return 文件列表的长度
*/
@Override
public int getCount() {
return files.length;
}

/**
* 4.2.2 获得特定位置的数据
*
* @param position 位置
* @return 特定位置的数据
*/
@Override
public Object getItem(int position) {
return files[position];
}

/**
* 4.2.3 获得特定位置数据的 id (使用SQLite的场景)
*
* @param position 位置
* @return 特定位置数据的 id
*/
@Override
public long getItemId(int position) {
// 位置是列表项在 ListView 中的索引
// 在排序规则改变时,位置的数值会改变
// id 是数据的唯一标识,不会改变
return 0;
}

/**
* 4.3 获得特定位置加载了数据的视图(列表项)
*
* @param position 位置
* @param convertView 可重用的视图
* @param parent 父元素
* @return 列表项
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
ImageButtonListener listener = null;

if (convertView == null) {
// 若无【可重用的视图】,才实例化 xml 文件创建视图
// inflate 方法执行的次数为屏幕上能显示的列表项的最大值
convertView = inflater.inflate(R.layout.file_item, parent, false);

// 创建【结构持有者】,获得视图中的各个控件的作用
holder = new ViewHolder();
holder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
holder.btnOperator = (ImageButton) convertView.findViewById(R.id.btnOperator);
holder.tvFilename = (TextView) convertView.findViewById(R.id.tvFilename);
holder.tvSummary = (TextView) convertView.findViewById(R.id.tvSummary);

// 创建按钮的监听器
listener = new ImageButtonListener();
// 注册监听器
holder.btnOperator.setOnClickListener(listener);
// 将监听器存储【绑定】到按钮中
holder.btnOperator.setTag(listener);

// 将【结构持有者】存储到视图中
convertView.setTag(holder);
} else {
// 有【可重用的视图】,则从中取出它的【结构持有者】
holder = (ViewHolder) convertView.getTag();

// 获得按钮的监听器
listener = (ImageButtonListener) holder.btnOperator.getTag();
}

// 在【结构持有者】中加载 position 位置的数据
File file = files[position];

holder.tvFilename.setText(file.getName());
holder.tvSummary.setText(file.isFile()
? String.format(context.getString(R.string.fileSize), file.length())
: String.format(context.getString(R.string.contents), file.listFiles().length));
holder.imageView.setImageResource(file.isFile()
? R.drawable.ic_file
: R.drawable.ic_folder);

// 修改监听器的监听的数据
listener.setData(file);

// 返回视图
return convertView;
}

/**
* 4.3.1 列表项的 结构持有者
* 存储 file_item.xml 文件中的控件结构
* 直接通过字段访问,为提高性能
*/
private static class ViewHolder {
ImageView imageView;
ImageButton btnOperator;
TextView tvFilename;
TextView tvSummary;
}

/**
* 4.3.2 FileAdapter 的内部类
* 自定义的 ImageButton 点击监听器
*/
private class ImageButtonListener implements View.OnClickListener {
// 点击时获得的数据(文件/目录)
private File data;

public void setData(File data) {
this.data = data;
}

@Override
public void onClick(View v) {
// 创建弹出菜单
PopupMenu menu = new PopupMenu(context, v);
// 加载菜单文件
menu.inflate(R.menu.popup);
// 注册菜单选项事件
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
String text = "";
switch (item.getItemId()) {
case R.id.action_copy:
// copyFile();
text = context.getString(R.string.copy);
break;
case R.id.action_delete:
// deleteFile();
text = context.getString(R.string.delete);
}
text += ": " + data.getName();
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
return true;
}
});
// 显示菜单
menu.show();
}
}
}


[size=medium](9)popup.xml[/size]

 <?xml version="1.0" encoding="utf-8"?>  
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/action_copy"
android:title="@string/copy"/>

<item
android:id="@+id/action_delete"
android:title="@string/delete"/>
</menu>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值