当前位置: 代码迷 >> Android >> 【攻占Android (12)】BaseAdapter 自定义适配器
  详细解决方案

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

热度:134   发布时间:2016-04-27 23:46:45.0
【攻克Android (12)】BaseAdapter 自定义适配器
本文围绕以下三个部分展开:

一、BaseAdapter 自定义适配器
二、一个案例

附   代码补充






一、BaseAdapter 自定义适配器

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




二、一个案例









        案例说明:

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

        目录:显示出目录(文件夹)的图标、目录名、包含几个文件。文件:显示出文件的图标、文件名、文件大小。

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


        1. 在 styles.xml(v21) 中设置标题栏颜色。

 <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>


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

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


        3. 启动后,会先启动MainAcitivity,然后加载主活动的布局文件(主界面)。

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


        因此要写主界面 activity_main.xml :列表框(ListView)。

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


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

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


        然后再创建里面的布局文件:file_item.xml ,然后写布局。

 <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"/>


        4. 判断SD卡状态,并读取SD卡内容。

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

		 // 获得当前设备的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();           }


        然后在手机上部署,显示以下效果,说明SD卡正在使用。



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

 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();           }       }


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

 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();           }       }


        其中,用到了SD卡文件过滤器 SdFileFilter ,因此需创建此文件过滤器。

 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;       }   }


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

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

 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();           }       }


        6. 进入 FileAdapter 类,写该文件适配器。

        (1)创建构造方法。

 /**   * 自定义文件适配器类   * 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);       }   }


        (2)继承了 BaseAdapter 类,要重写(override)四个方法。

        (2.1)重写 getCount() 方法。

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


        (2.2)重写 getItem(int position) 方法。

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


        (2.3)重写 getItemId(int position) 方法。

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


        (2.4)重写 getView(int position, View convertView, ViewGroup parent) 方法。

 /**   * (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;   }


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

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


        (2.4.1)列表项的【结构持有者】:ViewHolder 。

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


        (2.4.2)自定义的 ImageButton 点击监听器:ImageButtonListener 。

 /**   * (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();       }   }


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

        上面用到了弹出菜单 PopupMenu ,因此要创建弹出菜单: popup.xml。

 <?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>



附   代码补充

        项目目录结构如下:



        (1)styles.xml(v21)

 <?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>


        (2)AndroidManifest.xml

 <?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>


        (3)strings.xml

 <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>


        (4)activity_main.xml

 <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>


        (5)file_item.xml

 <?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>


        (6)MainActivity

 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);       }   }


        (7)SdFileFilter

 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;       }   }


        (8)FileAdapter

 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();           }       }   }


        (9)popup.xml

 <?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>

  相关解决方案