RecyclerView简介:
可以理解 RecyclerView 是 ListView 的升级版,更加灵活,同时由于封装了 ListView 的部分实现,导致其使用更简单,结构更清晰。
它直接提供了回收复用的功能,虽然 ListView 我们也可以自己实现 ViewHolder 以及 convertView 进行优化,但是在 RecyclerView 中,它直接封装了 ViewHolder 的回收复用,也就是说 RecyclerView 将 ViewHolder 标准化,我们不再需要面向 view ,而是直接面向 ViewHolder 编写实现我们需要的 Adapter,这样一来,逻辑结构就变得非常清晰。
当然,说到 RecyclerView 的优点,就不得不提它的 插拔式 的体验,高度解耦:
- 布局(显示方式):可通过LayoutManager(LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager )设置;
- 分割线:通过 ItemDecoration 实现
- Item 增删动画:通过 ItemAnimator
- ViewHolder 的创建和绑定:通过实现 Adapter
除此之外,还需要额外提一下关于点击事件的问题,RecyclerView 本身是不提供点击、长按事件的,而隔壁的 ListView 稳稳支持。对此,可能刚接触 RecyclerView 的同学会疯狂吐槽,怎么作为升级版的 RecyclerView 在这一点上还不如旧版呢?
情况真的是这样么?
显然不是。
ListView 中对于点击事件的处理,其实是有很大弊端的,它的 setOnItemClickListener() 方法是为子项注册点击事件,这就导致只能识别到这一整个子项,对于子项中的组件比如按钮就束手无策了。为此,RecyclerView 直接放弃了这个为子项注册点击事件的监听方法,所有点击事件都有具体 View 去注册,好处显而易见,我可以按需为组件注册点击事件,不存在点击不到的组件
下面就来为我们的RecyclerView注册绑定点击、长按事件
注意:下例使用的是我在工作过程中的一些功能实例,数据提交的可自行删除!!!(博主懒删直接整个实例拷贝过来)
新建点击接口 ClickListener :
fileName 是点击后我们要用到或者想要的数据,如果传入适配器的是数组,建议返回数组的单位数据
public interface PhotoClickListener { public void onPhotoClick(RecyclerView parent , View view , String fileName ); }
新建长按接口 LongClickListener :
public interface PhotoLongClickListener { public void onPhotoLongClick(RecyclerView parent , View view , String fileName); }
我们的主视图布局 main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.hsjgappzjj.DataUploadActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimaryLight" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="15dp" android:layout_marginTop="15dp" android:text="非工况法材料上传" android:textColor="#FFFFFF" android:textSize="25dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:orientation="horizontal" android:gravity="center"> <TextView android:id="@+id/text_tips" android:layout_width="0dp" android:layout_height="50dp" android:layout_weight="1" android:background="@drawable/btn_selector" android:gravity="center" android:text="审核状态:" android:textColor="@color/white_overlay" android:textSize="20dp" /> <Spinner android:id="@+id/resultState" android:layout_width="0dp" android:layout_height="50dp" android:layout_marginLeft="5dp" android:layout_weight="1" android:background="@drawable/btn_selector" android:textColor="@color/red_overlay" /> </LinearLayout> <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/photoRecyclerView" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView> <Button android:id="@+id/photoUploadBtn" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/btn_selector" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:textColor="@color/red_overlay" android:text="提交"/> </LinearLayout> </ScrollView> </LinearLayout>
RecyclerView 自定义适配器:
public class DataUploadAdapter extends RecyclerView.Adapter<DataUploadAdapter.myViewHolder> implements View.OnClickListener , View.OnLongClickListener{ private Context context; private RecyclerView recyclerView; private String Jylsh; private String[] photoNames;private PhotoClickListener photoClickListener = null; private PhotoLongClickListener photoLongClickListener = null; /**【构造函数】**/ public DataUploadAdapter(Context context , String Jylsh , RecyclerView recyclerView){ this.context = context; this.recyclerView = recyclerView; this.Jylsh = Jylsh; initPhotoData(); } /**【点击监听】**/ public void setPhotoClickListener(PhotoClickListener clickListener){ this.photoClickListener = clickListener; } /**【长按监听】**/ public void setPhotoLongClickListener(PhotoLongClickListener longClickListener){ this.photoLongClickListener = longClickListener; } @Override public myViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.dataupload_item,parent,false); final myViewHolder holder = new myViewHolder(view); /**【点击事件】**/ view.setOnClickListener(this); /**【长按事件】**/ view.setOnLongClickListener(this); return holder; } /**【子项赋值】**/ @Override public void onBindViewHolder(myViewHolder holder, int position) { holder.photoNameText.setText(photoNames[position]); holder.uploadStates.setText("已上传"); String filePath = Environment.getExternalStorageDirectory() + "/" + "MyVehPhoto/"; //照片目录 String fileName = Jylsh + "/" + photoNames[position] +".jpg"; //照片流水号下的照片名称 //Log.e("完整照片目录",filePath + fileName); Bitmap imgBitmap = BitmapFactory.decodeFile(filePath + fileName); holder.takePhotoImg.setImageBitmap(imgBitmap); } /**【子项总数量】**/ @Override public int getItemCount() { return photoNames.length; } /**【点击事件】**/ @Override public void onClick(View view) { TextView textView = view.findViewById(R.id.photoNameText); String fileName = textView.getText().toString() + ""; if (photoClickListener != null){ photoClickListener.onPhotoClick(recyclerView , view ,fileName); } } /**【长按事件】**/ @Override public boolean onLongClick(View view) { TextView textView = view.findViewById(R.id.photoNameText); String fileName = textView.getText().toString() + ""; if (photoLongClickListener != null){ photoLongClickListener.onPhotoLongClick(recyclerView , view , fileName); } return false; } /**【自定义类中的组件】**/ class myViewHolder extends RecyclerView.ViewHolder{ private ImageView takePhotoImg; private TextView photoNameText , uploadStates; public myViewHolder(View itemView) { super(itemView); takePhotoImg = itemView.findViewById(R.id.takePhotoImg); photoNameText = itemView.findViewById(R.id.photoNameText); uploadStates = itemView.findViewById(R.id.uploadStates); } } /**【获取照片名字以及上传状态】**/ private void initPhotoData(){ SharedPreferences photoData = context.getSharedPreferences("photoData", 0); String photoName = photoData.getString("photoNames",""); if (!photoName.equals("")){ photoNames = photoName.split(","); } } }
每一个子项布局 dataupload_item.xml :
<?xml version="1.0" encoding="utf-8"?> <!-- 数据展示页面子项布局 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="150dp" android:id="@+id/uploadLayout" android:orientation="vertical" android:layout_marginTop="2dp" android:layout_marginBottom="3dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:gravity="center"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <!-- android:src="@drawable/login_icon" --> <ImageView android:id="@+id/takePhotoImg" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/bootstrap_gray_dark"> </ImageView> <TextView android:id="@+id/photoNameText" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="15sp" android:textColor="@color/white_overlay" android:layout_alignBottom="@id/takePhotoImg" android:text="照片名称"/> <TextView android:id="@+id/uploadStates" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:textColor="@color/red_overlay" android:textSize="15sp" android:text="上传状态"/> </RelativeLayout> </LinearLayout>
主要处理代码 main.java
public class DataUploadActivity extends BaseActivity { private RecyclerView recyclerView; private Button photoUploadBtn; private DataUploadAdapter adapter; private String clickFileName , photoName; private String Jylsh ; //"20210202008" private String ip , jkxlh; private Spinner resultState; private String status = "1" , selItemStr = ""; //审核状态 private String uploadStatus = ""; private Q11Domain theinformation; //车辆信息项目 private static final int MSG_SUCCESS = 2087; private static final int MSG_SHOW = 2088; private static final int MSG_DISMISS = 2089; private static final int MSG_ERROR = 2090; private ProgressDialog builder = null; private Message message; @SuppressLint("HandlerLeak") private Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == MSG_SHOW) { if (builder == null){ builder = new ProgressDialog(DataUploadActivity.this); builder.setMessage(msg.obj.toString()); builder.setCancelable(false); builder.show(); }else { builder.show(); } }else if (msg.what == MSG_DISMISS) { if (builder != null){ builder.dismiss(); } }else if (msg.what == MSG_ERROR) { initAdapter(); DialogTool.AlertDialogShow(DataUploadActivity.this, msg.obj.toString()); }else if (msg.what == MSG_SUCCESS){ initAdapter(); ToastUtil.showAnimaToast(msg.obj.toString()); } } }; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏 super.onCreate(savedInstanceState); setContentView(R.layout.data_upload); SharedPreferences photoData = getSharedPreferences("photoData", 0); SharedPreferences.Editor editor = photoData.edit(); String photoNames = "申请图片,佐证材料1,佐证材料2,佐证材料3,佐证材料4"; editor.putString("photoNames",photoNames); //String uploadStates = "1,0,1,0,1"; //editor.putString("uploadStates",uploadStates); editor.commit(); initAdapter(); } /**【适配器初始化】**/ private void initAdapter(){ theinformation = (Q11Domain) getIntent().getExtras().getSerializable("informationsObj"); //车辆信息 if (theinformation != null){ Jylsh = theinformation.getJylsh(); }else { Jylsh = "20210202008"; } createUploadStatusFile(Jylsh , ""); //创建初始化记录照片上传状态 SharedPreferences preferences = getSharedPreferences("cs_setup", 0); ip = preferences.getString("IP", ""); jkxlh = preferences.getString("JKXLH", ""); resultState = findViewById(R.id.resultState); final String [] selectItems = {"审核通过","审核不通过"}; ArrayAdapter<String> selItem = new ArrayAdapter<>(this,android.R.layout.simple_list_item_single_choice,selectItems); resultState.setAdapter(selItem); resultState.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { selItemStr = selectItems[i]; if (selItemStr.equals("审核不通过")){ status = "2"; }else if (selItemStr.equals("审核通过")){ status = "1"; } } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }); /**【网格布局】**/ recyclerView = findViewById(R.id.photoRecyclerView); GridLayoutManager gridlayout = new GridLayoutManager(this,2); recyclerView.setLayoutManager(gridlayout); adapter = new DataUploadAdapter(DataUploadActivity.this,Jylsh,recyclerView); /**【点击事件】**/ adapter.setPhotoClickListener(new PhotoClickListener() { @Override public void onPhotoClick(RecyclerView parent, View view, String fileName) { File file = new File(createFile(Jylsh , fileName)); if (!file.exists()){ //判断是否存在照片 ToastUtil.showAnimaToast("照片未拍摄,请长按拍照!"); return; } Intent intentClick = new Intent(DataUploadActivity.this,ImageShowActivity.class); clickFileName = createFile(Jylsh , fileName); //完整的文件名 photoName = fileName; intentClick.putExtra("photoType", fileName); //当前照片名称 intentClick.putExtra("imageFullPath", clickFileName); //照片完整目录和名称 startActivity(intentClick); } }); /**【长按事件】**/ adapter.setPhotoLongClickListener(new PhotoLongClickListener() { @Override public void onPhotoLongClick(RecyclerView parent, View view, String fileName) { Toast.makeText(DataUploadActivity.this,fileName,Toast.LENGTH_LONG).show(); Intent intentLongClick = new Intent("android.media.action.IMAGE_CAPTURE"); clickFileName = createFile(Jylsh , fileName); //完整的文件名 photoName = fileName; File file = new File(clickFileName); //file:新建一个文件 Uri uri = Uri.fromFile(file); //将File文件转换成Uri以启动相机程序 intentLongClick.putExtra(MediaStore.EXTRA_OUTPUT,uri); //指定图片输出地址 startActivityForResult(intentLongClick,88); } }); recyclerView.setAdapter(adapter); photoUploadBtn = findViewById(R.id.photoUploadBtn); photoUploadBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.e("审核结果",status); request02C31(); Toast.makeText(DataUploadActivity.this,"提交数据",Toast.LENGTH_LONG).show(); } }); } @Override protected void onResume() { super.onResume(); initAdapter(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK){ switch (requestCode){ case 88: FileInputStream inputStream = null; try { inputStream = new FileInputStream(clickFileName); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); //获取指定目录下保存的位图 OutputStream os = new FileOutputStream(clickFileName); bitmap.compress(Bitmap.CompressFormat.JPEG,50,os); os.flush(); os.close(); inputStream.close(); photoUpload(clickFileName ,photoName); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } break; } } } public void sendMessages(int msgNumber, String Mesg) { message = new Message(); message.what = msgNumber; message.obj = Mesg; handler.sendMessage(message); } /**【创建文件目录,并返回完整的文件路径和名称】**/ private String createFile(String Jylsh , String fileName){ String filePath = Environment.getExternalStorageDirectory() + "/MyVehPhoto/" + Jylsh + "/"; //照片目录 File folder = new File(filePath); if (!folder.exists()){ folder.mkdirs(); } String name = filePath + fileName + ".jpg"; //Log.e("完整的照片名称",name); return name; } /**【获取照片字符串】**/ private String getPhotoData(String fileName){ File file = new File(fileName); String photo = ""; if (!file.exists()){ //如果图片不存在 return photo; } Bitmap bitmap = BitmapFactory.decodeFile(fileName); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG,80,outputStream); byte [] bytes = outputStream.toByteArray(); photo = Base64.encodeToString(bytes,Base64.DEFAULT); Log.e("图片压缩64位字符串",photo); return photo; } /**【上传照片】**/ private void photoUpload(final String updateFileName , final String photoName){ new Thread(new Runnable() { @Override public void run() { sendMessages(MSG_SHOW , "正在上传中,请稍等。。。"); String photoData = getPhotoData(updateFileName); //获取图片base64字符串数据 String photoXML = UnXmlTool.uploadPhotoXml(Jylsh,photoData,getPhotoName(photoName)); String photoInfo = ConnectMethods.connectWebService(ip, StaticValues.queryObject, jkxlh, "02C32", photoXML, StaticValues.queryResult, StaticValues.timeoutThree, StaticValues.numberFive); Log.e("照片上传返回结果",photoInfo); List<Code> codeList = XMLParsingMethods.saxcode(photoInfo); if (codeList.get(0).getCode().equals("1")){ handler.sendEmptyMessage(MSG_DISMISS); String fileName = Environment.getExternalStorageDirectory()+"/MyVehPhoto/"+Jylsh+"/uploadStatus.txt"; File fileStatus = new File(fileName); if (fileStatus.exists()){ //判断记录状态文件是否存在 String content = DocumentTool.readFileContent(fileName); if (!content.equals("")){ uploadStatus = volidateFileData(photoName,photoData,content); createUploadStatusFile(Jylsh , uploadStatus); } } sendMessages(MSG_SUCCESS , "照片上传成功!"); }else{ handler.sendEmptyMessage(MSG_DISMISS); sendMessages(MSG_ERROR , codeList.get(0).getMessage()); } } }).start(); } /**【提交数据】**/ private void request02C31(){ new Thread(new Runnable() { @Override public void run() { sendMessages(MSG_SHOW , "正在上传中,请稍等。。。"); String photoXML = UnXmlTool.get02C31XML(Jylsh,status,"","","","",""); String photoData = ConnectMethods.connectWebService(ip, StaticValues.queryObject, jkxlh, "02C31", photoXML, StaticValues.queryResult, StaticValues.timeoutThree, StaticValues.numberFive); List<Code> codeList = XMLParsingMethods.saxcode(photoData); if (codeList.get(0).getCode().equals("1")){ handler.sendEmptyMessage(MSG_DISMISS); sendMessages(MSG_SUCCESS , "照片上传成功!"); }else{ handler.sendEmptyMessage(MSG_DISMISS); sendMessages(MSG_ERROR , codeList.get(0).getMessage()); } } }).start(); } /**【创建文件记录照片上传状态】**/ private void createUploadStatusFile(String lsh , String updateContent){ try { String fileName = Environment.getExternalStorageDirectory()+"/MyVehPhoto/"+lsh+"/uploadStatus.txt"; File fileStatus = new File(fileName); if (!fileStatus.exists()) { //文件不存在时 fileStatus.createNewFile(); updateContent = "apply:0-evidenceOne:0-evidenceTwo:0-evidenceThree:0-evidenceFour:0"; DocumentTool.writeData(fileStatus.getPath() , updateContent); }else{ if (!updateContent.equals("")){ DocumentTool.writeData(fileStatus.getPath() , updateContent); } } String fileContent = DocumentTool.readFileContent(fileName); Log.e("uploadStatus的文件内容",fileContent); } catch (IOException e) { e.printStackTrace(); } } /**【判断是哪张照片的数据、更新照片上传状态】**/ private String volidateFileData(String fileName , String photoData ,String content){ String[] listStatus = content.split("-"); //"apply:0" String result = ""; if (fileName.contains("申请图片")){ if (!photoData.equals("")){ listStatus[0] = "apply:1"; } }else if (fileName.contains("佐证材料1")){ if (!photoData.equals("")){ listStatus[1] = "evidenceOne:1"; } }else if (fileName.contains("佐证材料2")){ if (!photoData.equals("")){ listStatus[2] = "evidenceTwo:1"; } }else if (fileName.contains("佐证材料3")){ if (!photoData.equals("")){ listStatus[3] = "evidenceThree:1"; } }else if (fileName.contains("佐证材料4")){ if (!photoData.equals("")){ listStatus[4] = "evidenceFour:1"; } } if (listStatus.length > 0){ for (int i = 0 ; i < listStatus.length ; i ++){ result = result + listStatus[i] + "-"; } } result = result.substring(0,result.length() - 1); Log.e("重新合成的数据",result); return result; } /**【生成对应的文件名:photoName】**/ private String getPhotoName(String name){ String result = ""; if (name != null && !name.equals("")){ if (name.equals("申请图片")){ result = "SQPIC"; }else if (name.equals("佐证材料1")){ result = "szZP1"; }else if (name.equals("佐证材料2")){ result = "szZP2"; }else if (name.equals("佐证材料3")){ result = "szZP3"; }else if (name.equals("佐证材料4")){ result = "szZP4"; } } return result; } }
看下布局结果,具体的点击事件可以自行修改,长按我用来启动手机相机拍照:
到这里我要做的也完成了,有问题欢迎提出探讨指正。。。