Android自定義View實(shí)現(xiàn)數(shù)字密碼鎖
最近項(xiàng)目上用到一個(gè)密碼加鎖功能,需要一個(gè)數(shù)字密碼界面,就想著封裝成一個(gè)View來方便管理和使用。
廢話不多說,先上最終效果圖:
思路
整體可分為2個(gè)部分來實(shí)現(xiàn),1.頂部是4個(gè)密碼位的填充;2.數(shù)字鍵盤部分。整體可以是一個(gè)縱向LinearLayout,4個(gè)密碼位用橫向LinearLayout即可,鍵盤由于是宮格形式,因此可用GridLayout來布局。由于密碼位和鍵盤數(shù)字都是以圓圈為背景,這里采用自定義一個(gè)圓形背景ImageView來使用。
實(shí)現(xiàn)
1.頁面布局
首先定義一個(gè)圓形背景的ImageView,由于最終實(shí)現(xiàn)的效果是點(diǎn)擊的時(shí)候要填充圓背景,非點(diǎn)擊狀態(tài)下是空心圓,因此可通過改變Paint的style來動(dòng)態(tài)更改顯示:
/** * 圓形背景ImageView(設(shè)置實(shí)心或空心) */ public class CircleImageView extends ImageView{ private Paint mPaint; private int mWidth; private int mHeight; public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } public void initView(Context context){ mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(mPanelColor); mPaint.setStrokeWidth(mStrokeWidth); mPaint.setAntiAlias(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; } @Override public void draw(Canvas canvas) { canvas.drawCircle(mWidth/2, mHeight/2, mWidth/2 - 6, mPaint); super.draw(canvas); } /** * 設(shè)置圓為實(shí)心狀態(tài) */ public void setFillCircle(){ mPaint.setStyle(Paint.Style.FILL); invalidate(); } /** * 設(shè)置圓為空心狀態(tài) */ public void setStrokeCircle(){ mPaint.setStyle(Paint.Style.STROKE); invalidate(); } }
可以看到,在onDraw中繪制了一個(gè)圓,默認(rèn)為空心狀態(tài),定義setFillCircle和setStrokeCircle這兩個(gè)方法以便外界可以方便地切換圓為實(shí)心或者空心。
圓形ImageView定義好了,開始添加密碼位,布局如下:
inputResultView = new LinearLayout(context); for(int i=0; i<4; i++){ CircleImageView mResultItem = new CircleImageView(context); mResultIvList.add(mResultItem); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mResultIvRadius, mResultIvRadius); params.leftMargin = dip2px(context, 4); params.rightMargin = dip2px(context, 4); mResultItem.setPadding(dip2px(context, 2),dip2px(context, 2),dip2px(context, 2),dip2px(context, 2)); mResultItem.setLayoutParams(params); inputResultView.addView(mResultItem); } LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER_HORIZONTAL; params.bottomMargin = dip2px(context, 34); inputResultView.setLayoutParams(params); addView(inputResultView);
接著添加數(shù)字鍵盤部分的布局:
GridLayout numContainer = new GridLayout(context); numContainer.setColumnCount(3); for(int i=0; i<numArr.length; i++){ RelativeLayout numItem = new RelativeLayout(context); numItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom); RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius); gridItemParams.addRule(CENTER_IN_PARENT); final TextView numTv = new TextView(context); numTv.setText(numArr[i]); numTv.setTextColor(mPanelColor); numTv.setTextSize(30); numTv.setGravity(Gravity.CENTER); numTv.setLayoutParams(gridItemParams); final CircleImageView numBgIv = new CircleImageView(context); numBgIv.setLayoutParams(gridItemParams); numItem.addView(numBgIv); numItem.addView(numTv); numContainer.addView(numItem); if(i == 9){ numItem.setVisibility(INVISIBLE); } } //刪除按鈕 RelativeLayout deleteItem = new RelativeLayout(context); deleteItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom); RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius); gridItemParams.addRule(CENTER_IN_PARENT); //假如刪除按鈕是設(shè)置自定義圖片資源的話,可用注釋這段 //ImageView deleteIv = new ImageView(context); //deleteIv.setImageResource(R.drawable.icn_delete_pw); //deleteIv.setLayoutParams(gridItemParams); //deleteItem.addView(deleteIv); TextView deleteTv = new TextView(context); deleteTv.setText("Delete"); deleteTv.setTextColor(mPanelColor); deleteTv.setTextSize(dip2px(context, 8)); deleteTv.setLayoutParams(gridItemParams); deleteTv.setGravity(Gravity.CENTER); deleteItem.addView(deleteTv); numContainer.addView(deleteItem); LinearLayout.LayoutParams gridParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); gridParams.gravity = Gravity.CENTER_HORIZONTAL; numContainer.setLayoutParams(gridParams); addView(numContainer);
數(shù)字鍵盤這里用一個(gè)數(shù)組存數(shù)字內(nèi)容,遍歷添加,注意此處由于第10個(gè)的子View的時(shí)候是空白的,所以當(dāng)遍歷到第10個(gè)元素的時(shí)候,可以將其隱藏。遍歷完后再單獨(dú)添加刪除按鈕。
2.輸入邏輯
頁面布局完成了,接下來就是密碼輸入的邏輯部分,最終的效果是每點(diǎn)擊一次數(shù)字,密碼位就填充一個(gè),每點(diǎn)擊刪除按鈕一次,密碼位就回退一個(gè),輸入4個(gè)數(shù)字之后,即完成輸入,獲取結(jié)果,并重置密碼位。這里用一個(gè)StringBuilder變量來記錄當(dāng)前已輸入的密碼,每次添加就append進(jìn)去,每次刪除就調(diào)用deleteCharAt。
由于點(diǎn)擊數(shù)字按下的時(shí)候填充,松開的時(shí)候?yàn)榭招臓顟B(tài),所以可以在ACTION_DOWN和ACTION_UP事件中分別操作:
numTv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: numBgIv.setFillCircle(); numTv.setTextColor(Color.WHITE); if(mPassWord.length() < 4){ mPassWord.append(numTv.getText()); mResultIvList.get(mPassWord.length()-1).setFillCircle(); if(mInputListener!=null && mPassWord.length() == 4){ //已完整輸入4個(gè) } } break; case MotionEvent.ACTION_UP: numBgIv.setStrokeCircle(); numTv.setTextColor(mPanelColor); break; } return true; } });
每次點(diǎn)擊的時(shí)候,判斷當(dāng)前已輸入的密碼位是否已經(jīng)超過4位,如果沒超過,就繼續(xù)追加。如果等于4,就說明輸入完成,此時(shí)的mPassWord的內(nèi)容就是最終的密碼,可以用一個(gè)接口將其回調(diào)出去方便Activity中獲取輸入的密碼:
/** * 監(jiān)聽輸入完畢的接口 */ private InputListener mInputListener; public void setInputListener(InputListener mInputListener) { his.mInputListener = mInputListener; } public interface InputListener{ void inputFinish(String result); }
然后在上面的ACTION_DOWN中輸入數(shù)字等于4的時(shí)候,回調(diào)該接口:
if(mInputListener!=null && mPassWord.length() == 4){ mInputListener.inputFinish(mPassWord.toString()); }
另外,刪除的操作單獨(dú)封裝為一個(gè)方法:
/** * 刪除 */ public void delete(){ if(mPassWord.length() == 0){ return; } mResultIvList.get(mPassWord.length()-1).setStrokeCircle(); mPassWord.deleteCharAt(mPassWord.length()-1); }
注意點(diǎn):當(dāng)前無輸入密碼時(shí),直接return不作任何操作,假如已有輸入數(shù)字,就刪除最尾部的那個(gè)數(shù)字。
最后,還要考慮一種情況,即用戶輸入密碼錯(cuò)誤時(shí)的一些反饋,參照平時(shí)的習(xí)慣,一般是4個(gè)密碼位左右擺動(dòng)并且手機(jī)震動(dòng)效果,震動(dòng)結(jié)束之后,當(dāng)前存儲(chǔ)的密碼位重置為初始狀態(tài),如下:
/** * 輸入錯(cuò)誤的狀態(tài)顯示(包括震動(dòng),密碼位左右搖擺效果,重置密碼位) */ public void showErrorStatus(){ mVibrator.vibrate(new long[]{100,100,100,100},-1); List<Animator> animators = new ArrayList<>(); ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(inputResultView, "translationX", -50.0f,50.0f,-50.0f,0.0f); translationXAnim.setDuration(400); animators.add(translationXAnim); AnimatorSet btnSexAnimatorSet = new AnimatorSet(); btnSexAnimatorSet.playTogether(animators); btnSexAnimatorSet.start(); btnSexAnimatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { resetResult(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); }
可以看到,在onAnimationEnd中調(diào)用了resetResult,即動(dòng)畫結(jié)束時(shí)重置密碼,resetResult方法如下:
/** * 重置密碼輸入 */ public void resetResult(){ for(int i=0; i<mResultIvList.size(); i++){ mResultIvList.get(i).setStrokeCircle(); } mPassWord.delete(0, 4); }
遍歷所有密碼位View設(shè)置為空心,并且刪除當(dāng)前mPassWord變量存儲(chǔ)的所有內(nèi)容。
完整代碼
完整的自定義數(shù)字密碼鎖代碼如下:
package com.example.zjyang.viewtest.view; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Service; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Vibrator; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import static android.widget.RelativeLayout.CENTER_HORIZONTAL; import static android.widget.RelativeLayout.CENTER_IN_PARENT; /** * Created by IT_ZJYANG on 2018/1/22. * 數(shù)字解鎖鍵盤View */ public class NumLockPanel extends LinearLayout { private String[] numArr = new String[]{"1","2","3","4","5","6","7","8","9", "", "0"}; private int mPaddingLeftRight; private int mPaddingTopBottom; //4個(gè)密碼位ImageView private ArrayList<CircleImageView> mResultIvList; private LinearLayout inputResultView; //存儲(chǔ)當(dāng)前輸入內(nèi)容 private StringBuilder mPassWord; //振動(dòng)效果 private Vibrator mVibrator; //整個(gè)鍵盤的顏色 private int mPanelColor; //4個(gè)密碼位的寬度 private int mResultIvRadius; //數(shù)字鍵盤的每個(gè)圓的寬度 private int mNumRadius; //每個(gè)圓的邊界寬度 private int mStrokeWidth; public NumLockPanel(Context context) { this(context, null); } public NumLockPanel(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NumLockPanel(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaddingLeftRight = dip2px(context, 21); mPaddingTopBottom = dip2px(context, 10); mPanelColor = Color.BLACK; //顏色代碼可采用Color.parse("#000000"); mResultIvRadius = dip2px(context, 20); mNumRadius = dip2px(context, 66); mStrokeWidth = dip2px(context, 2); mVibrator = (Vibrator)context.getSystemService(Service.VIBRATOR_SERVICE); mResultIvList = new ArrayList<>(); mPassWord = new StringBuilder(); setOrientation(VERTICAL); setGravity(CENTER_HORIZONTAL); initView(context); } public void initView(Context context){ //4個(gè)結(jié)果號(hào)碼 inputResultView = new LinearLayout(context); for(int i=0; i<4; i++){ CircleImageView mResultItem = new CircleImageView(context); mResultIvList.add(mResultItem); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mResultIvRadius, mResultIvRadius); params.leftMargin = dip2px(context, 4); params.rightMargin = dip2px(context, 4); mResultItem.setPadding(dip2px(context, 2),dip2px(context, 2),dip2px(context, 2),dip2px(context, 2)); mResultItem.setLayoutParams(params); inputResultView.addView(mResultItem); } LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER_HORIZONTAL; params.bottomMargin = dip2px(context, 34); inputResultView.setLayoutParams(params); addView(inputResultView); //數(shù)字鍵盤 GridLayout numContainer = new GridLayout(context); numContainer.setColumnCount(3); for(int i=0; i<numArr.length; i++){ RelativeLayout numItem = new RelativeLayout(context); numItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom); RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius); gridItemParams.addRule(CENTER_IN_PARENT); final TextView numTv = new TextView(context); numTv.setText(numArr[i]); numTv.setTextColor(mPanelColor); numTv.setTextSize(30); numTv.setGravity(Gravity.CENTER); numTv.setLayoutParams(gridItemParams); final CircleImageView numBgIv = new CircleImageView(context); numBgIv.setLayoutParams(gridItemParams); numTv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: numBgIv.setFillCircle(); numTv.setTextColor(Color.WHITE); if(mPassWord.length() < 4){ mPassWord.append(numTv.getText()); mResultIvList.get(mPassWord.length()-1).setFillCircle(); if(mInputListener!=null && mPassWord.length() == 4){ mInputListener.inputFinish(mPassWord.toString()); } } break; case MotionEvent.ACTION_UP: numBgIv.setStrokeCircle(); numTv.setTextColor(mPanelColor); break; } return true; } }); numItem.addView(numBgIv); numItem.addView(numTv); numContainer.addView(numItem); if(i == 9){ numItem.setVisibility(INVISIBLE); } } //刪除按鈕 RelativeLayout deleteItem = new RelativeLayout(context); deleteItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom); RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius); gridItemParams.addRule(CENTER_IN_PARENT); //假如刪除按鈕是設(shè)置自定義圖片資源的話,可用注釋這段 //ImageView deleteIv = new ImageView(context); //deleteIv.setImageResource(R.drawable.icn_delete_pw); //deleteIv.setLayoutParams(gridItemParams); //deleteItem.addView(deleteIv); TextView deleteTv = new TextView(context); deleteTv.setText("Delete"); deleteTv.setTextColor(mPanelColor); deleteTv.setTextSize(dip2px(context, 8)); deleteTv.setLayoutParams(gridItemParams); deleteTv.setGravity(Gravity.CENTER); deleteItem.addView(deleteTv); numContainer.addView(deleteItem); deleteTv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { delete(); } }); LinearLayout.LayoutParams gridParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); gridParams.gravity = Gravity.CENTER_HORIZONTAL; numContainer.setLayoutParams(gridParams); addView(numContainer); } /** * 輸入錯(cuò)誤的狀態(tài)顯示(包括震動(dòng),密碼位左右搖擺效果,重置密碼位) */ public void showErrorStatus(){ mVibrator.vibrate(new long[]{100,100,100,100},-1); List<Animator> animators = new ArrayList<>(); ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(inputResultView, "translationX", -50.0f,50.0f,-50.0f,0.0f); translationXAnim.setDuration(400); animators.add(translationXAnim); AnimatorSet btnSexAnimatorSet = new AnimatorSet(); btnSexAnimatorSet.playTogether(animators); btnSexAnimatorSet.start(); btnSexAnimatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { resetResult(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } /** * 刪除 */ public void delete(){ if(mPassWord.length() == 0){ return; } mResultIvList.get(mPassWord.length()-1).setStrokeCircle(); mPassWord.deleteCharAt(mPassWord.length()-1); } /** * 重置密碼輸入 */ public void resetResult(){ for(int i=0; i<mResultIvList.size(); i++){ mResultIvList.get(i).setStrokeCircle(); } mPassWord.delete(0, 4); } /** * 監(jiān)聽輸入完畢的接口 */ private InputListener mInputListener; public void setInputListener(InputListener mInputListener) { this.mInputListener = mInputListener; } public interface InputListener{ void inputFinish(String result); } /** * dip/dp轉(zhuǎn)像素 * * @param dipValue * dip或 dp大小 * @return 像素值 */ public static int dip2px(Context context, float dipValue) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return (int) (dipValue * (metrics.density) + 0.5f); } /** * 圓形背景ImageView(設(shè)置實(shí)心或空心) */ public class CircleImageView extends ImageView{ private Paint mPaint; private int mWidth; private int mHeight; public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } public void initView(Context context){ mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(mPanelColor); mPaint.setStrokeWidth(mStrokeWidth); mPaint.setAntiAlias(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; } @Override public void draw(Canvas canvas) { canvas.drawCircle(mWidth/2, mHeight/2, mWidth/2 - 6, mPaint); super.draw(canvas); } /** * 設(shè)置圓為實(shí)心狀態(tài) */ public void setFillCircle(){ mPaint.setStyle(Paint.Style.FILL); invalidate(); } /** * 設(shè)置圓為空心狀態(tài) */ public void setStrokeCircle(){ mPaint.setStyle(Paint.Style.STROKE); invalidate(); } } }
使用
在Activity的布局文件中:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" tools:context="com.example.zjyang.viewtest.MainActivity"> <com.example.zjyang.viewtest.view.NumLockPanel android:id="@+id/num_lock" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"> </com.example.zjyang.viewtest.view.NumLockPanel> </RelativeLayout>
在代碼中監(jiān)聽輸入的密碼結(jié)果:
public class MainActivity extends AppCompatActivity { private NumLockPanel mNumLockPanel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mNumLockPanel = (NumLockPanel) findViewById(R.id.num_lock); mNumLockPanel.setInputListener(new NumLockPanel.InputListener() { @Override public void inputFinish(String result) { //此處result即為輸入結(jié)果 Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); //錯(cuò)誤效果示例 mNumLockPanel.showErrorStatus(); } }); } }
最后,在自定義View構(gòu)造方法中初始化了圓圓和數(shù)字的顏色風(fēng)格,以及空心圓的邊界粗細(xì)大小,可根據(jù)需求自行更改。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)打開手機(jī)淘寶并自動(dòng)識(shí)別淘寶口令彈出商品信息功能
最近項(xiàng)目經(jīng)理給我們安排一個(gè)活兒,基于Android開發(fā)實(shí)現(xiàn)打開手機(jī)淘寶,并自動(dòng)識(shí)別淘口令,彈出商品信息,今天小編就抽空給大家分享下這個(gè)需求是怎么實(shí)現(xiàn)的,需要的朋友參考下吧2017-11-11Android中將View的內(nèi)容保存為圖像的簡單實(shí)例
這篇文章主要介紹了Android中將View的內(nèi)容保存為圖像的簡單實(shí)例,有需要的朋友可以參考一下2014-01-01Android中Service和Activity相互通信示例代碼
在android中Activity負(fù)責(zé)前臺(tái)界面展示,service負(fù)責(zé)后臺(tái)的需要長期運(yùn)行的任務(wù)。下面這篇文章主要給大家介紹了關(guān)于Android中Service和Activity相互通信的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09詳解Android使用CoordinatorLayout+AppBarLayout+CollapsingToolbarL
這篇文章主要為大家詳細(xì)介紹了Android使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayou實(shí)現(xiàn)手指滑動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05android控件Banner實(shí)現(xiàn)簡單輪播圖效果
這篇文章主要為大家詳細(xì)介紹了android控件Banner實(shí)現(xiàn)簡單輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05基于Alarmmanager實(shí)現(xiàn)簡單鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了基于Alarmmanager實(shí)現(xiàn)簡單鬧鐘功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android一步步帶你在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"功能
這篇文章主要介紹了Android一步步帶你在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"功能,需非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-03-03Android開發(fā)在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"-2
這篇文章主要介紹了Android開發(fā)在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"(二)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03