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; } }
看下布局結果,具體的點擊事件可以自行修改,長按我用來啟動手機相機拍照:
到這里我要做的也完成了,有問題歡迎提出探討指正。。。