日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android搜索框功能实现_Android实现滑动解锁功能

發(fā)布時間:2023/12/15 Android 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android搜索框功能实现_Android实现滑动解锁功能 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

說到滑動解鎖,就回到了2012~2014年,iPhone4S、5、5S年代,如今準備踏入2020年,這些年國產(chǎn)機崛起,再也不是公交車上都是iPhone4S的場景。本篇來使用ViewDragHelper實現(xiàn)滑動解鎖。

實現(xiàn)效果:

先來分析一下頁面的元素

  • 背景圖

  • 圓角滑道

  • 圓形滑塊

  • 閃動提示文字

  • 其他一些細節(jié):

  • 滑道和圓形滑塊之間有些邊距,我們使用padding來處理。

  • 我們需要自定義的就是第2點,這個滑道包含一個滑塊的圖片和提示文字,滑塊使用原生ImageView即可,而提示文字則是一個支持漸變著色的TextView(不是重點)。

    漸變著色的TextView

    先秒掉簡單的,漸變著色的TextView,不是重點,代碼量不多。

    public class ShineTextView extends TextView { private LinearGradient mLinearGradient; private Matrix mGradientMatrix; private int mViewWidth = 0; private int mTranslate = 0; public ShineTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mViewWidth == 0) { mViewWidth = getMeasuredWidth(); if (mViewWidth > 0) { Paint paint = getPaint(); mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{getCurrentTextColor(), 0xffffffff, getCurrentTextColor()}, null, Shader.TileMode.CLAMP); paint.setShader(mLinearGradient); mGradientMatrix = new Matrix(); } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mGradientMatrix != null) { mTranslate += mViewWidth / 5; if (mTranslate > 2 * mViewWidth) { mTranslate = -mViewWidth; } mGradientMatrix.setTranslate(mTranslate, 0); mLinearGradient.setLocalMatrix(mGradientMatrix); //每80毫秒執(zhí)行onDraw() postInvalidateDelayed(80); } }}

    下面重點介紹我們使用ViewDragHelper實現(xiàn)拽托、滑動的滑道View:SlideLockView

    讓滑塊滑動起來

    滑道實際就是一個FrameLayout,我們使用ViewDragHelper將滑塊ImageView進行拽托,主要我們要做以下幾件事:

    • 限制拽托的左側(cè)起點、右側(cè)終點(否則滑塊就出去啦!)

    • 松手時判斷滑塊的x坐標是偏向滑道的左側(cè)還是右側(cè),來決定滑動到起點還是終點。

    • 滾動結束,判斷是否到達了右側(cè)的終點。

    • 判斷拽托速度,如果超過指定速度,則自動滾動滑塊到右側(cè)終點。

    看到這4點,如果讓我們用事件分發(fā)來處理,代碼量和判斷會非常多,并且需要做速度檢測,而使用ViewDragHelper,上面4點都封裝好啦,我們添加一個回調(diào),再將事件委托給它,在回調(diào)中做事情上面4點的處理,一切都簡單起來了。

    • 創(chuàng)建SlideLockView,繼承FrameLayout

    public class SlideLockView extends FrameLayout { /** * 拽托幫助類 */ private ViewDragHelper mViewDragHelper; public SlideLockView(@NonNull Context context) { this(context, null); } public SlideLockView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SlideLockView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { //進行初始化... }}
    • 創(chuàng)建ViewDragHelper,使用create靜態(tài)方法創(chuàng)建,有3個參數(shù),第一個拽托控件的父控件(就是當前View),第二個參數(shù)是拽托靈敏度,數(shù)值越大,越靈敏,默認為1.0,第三個參數(shù)為回調(diào)對象。

    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { ... });}
    • 委托onInterceptTouchEvent、onTouchEvent事件給ViewDragHelper

    @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { //將onInterceptTouchEvent委托給ViewDragHelper return mViewDragHelper.shouldInterceptTouchEvent(ev);} @Overridepublic boolean onTouchEvent(MotionEvent event) { //將onTouchEvent委托給ViewDragHelper mViewDragHelper.processTouchEvent(event); return true;}
    • 找到布局中的滑塊,我們要求滑塊的id為lock_btn,所以需要在ids.xml中預先定義這個id,如果沒有查找到,則拋出異常。

    //文件名:ids.xml<?xml version="1.0" encoding="utf-8"?><resources> <item name="lock_btn" type="id" />resources>@Overrideprotected void onFinishInflate() { super.onFinishInflate(); //找到需要拽托的滑塊 mLockBtn = findViewById(R.id.lock_btn); if (mLockBtn == null) { throw new NullPointerException("必須要有一個滑動滑塊"); }}
    • 剩下的事情就在ViewDragHelper的回調(diào)中設置。

    復寫:

    ????????tryCaptureView()、

    ????? ? clampViewPositionHorizontal()、

    ????? ? clampViewPositionVertical()。

    • tryCaptureView為判斷子View是否可以拽托

    • clampViewPositionHorizontal()則是橫向拽托子View時回調(diào),返回可以拽托到的位置。

    • clampViewPositionVertical則是縱向拽托。

    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(@NonNull View child, int pointerId) { //判斷能拽托的View,這里會遍歷內(nèi)部子控件來決定是否可以拽托,我們只需要滑塊可以滑動 return child == mLockBtn; } @Override public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) { //拽托子View橫向滑動時回調(diào),回調(diào)的left,則是可以滑動的左上角x坐標 return left; } @Override public int clampViewPositionVertical(@NonNull View child, int top, int dy) { //拽托子View縱向滑動時回調(diào),鎖定頂部padding距離即可,不能不復寫,否則少了頂部的padding,位置就偏去上面了 return getPaddingTop(); } });}

    限制滑塊滑動范圍

    • 經(jīng)過上面3個方法重寫,滑塊已經(jīng)可以左右滑動了,但是可以滑動出滑道(父控件),我們需要限制橫向滑動的范圍,不能超過左側(cè)起點和右側(cè)終點。我們需要修改clampViewPositionHorizontal這個方法。

    • 左側(cè)起點的x坐標,就是paddingStart。

    • 右側(cè)終點,為滑道總長度 - 右邊邊距 - 滑塊寬度。

    • 判斷回調(diào)的left值,如果小于起點,則強制為起點,如果大于右側(cè)終點值,則強制為終點。

    這樣處理,滑塊則不會滑出滑道了!代碼量不對,也很清晰。

    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { //... @Override public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) { //拽托子View橫向滑動時回調(diào),回調(diào)的left,則是可以滑動的左上角x坐標 int lockBtnWidth = mLockBtn.getWidth(); //限制左右臨界點 int fullWidth = slideRail.getWidth(); //最少的左邊 int leftMinDistance = getPaddingStart(); //最多的右邊 int leftMaxDistance = fullWidth - getPaddingEnd() - lockBtnWidth; //修復兩端的臨界值 if (left < leftMinDistance) { return leftMinDistance; } else if (left > leftMaxDistance) { return leftMaxDistance; } return left; } //... });}

    松手回彈和速度檢測

    有了滑動和限制滑動范圍,我們還有一個松手回彈和速度檢測,ViewDragHelper同樣給我們封裝了,提供了一個onViewReleased()回調(diào),并且做了速度檢測,將速度也回傳給了我們。

    • 復寫onViewCaptured(),主要是為了獲取一開始捕獲到滑塊時,他的top值。

    • 復寫onViewReleased(),主要是計算松手時,滑塊比較近起點還比較近是終點,使用ViewDragHelper的settleCapturedViewAt()方法,開始彈性滾動滑塊去到起點或終點。

    • 判斷中,我們添加判斷速度是否超過1000,如果超過,即使拽托距離比較小,就當為fling操作,讓滑塊滾動到終點。

    settleCapturedViewAt()這個方法,內(nèi)部是使用Scroller進行彈性滾動的,所以我們需要復寫父View的computeScroll()方法,進行內(nèi)容滾動處理。

    如果不知道為什么這么做,搜索一下Scroller的資料了解一下~

    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { private int mTop; //... @Override public void onViewCaptured(@NonNull View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); //捕獲到拽托的View時回調(diào),獲取頂部距離 mTop = capturedChild.getTop(); } @Override public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); //獲取滑塊當前的位置 int currentLeft = releasedChild.getLeft(); //獲取滑塊的寬度 int lockBtnWidth = mLockBtn.getWidth(); //獲取滑道寬度 int fullWidth = slideRail.getWidth(); //一般滑道的寬度,用來判斷滑塊距離起點近還是終點近 int halfWidth = fullWidth / 2; //松手位置在小于一半,并且滑動速度小于1000,則回到左邊 if (currentLeft <= halfWidth && xvel < 1000) { mViewDragHelper.settleCapturedViewAt(getPaddingStart(), mTop); } else { //否則去到右邊(寬度,減去padding和滑塊寬度) mViewDragHelper.settleCapturedViewAt(fullWidth - getPaddingEnd() - lockBtnWidth, mTop); } invalidate(); } });}@Overridepublic void computeScroll() { super.computeScroll(); //判斷是否移動到頭了,未到頭則繼續(xù) if (mViewDragHelper != null) { if (mViewDragHelper.continueSettling(true)) { invalidate(); } }}

    解鎖回調(diào)

    經(jīng)過上面的編碼,滑動解鎖就完成了,但還差一個解鎖回調(diào),進行解鎖操作,并且我們需要一個時機知道滾動結束了(ViewDragHelper狀態(tài)回調(diào),滾動閑置了,并且滑塊位于終點,則為解鎖完成)。

    • 復寫onViewDragStateChanged()方法,處理ViewDragHelper狀態(tài)改變,狀態(tài)主要有以下3個:

  • STATE_IDLE = 0,滾動閑置,可以認為滾動停止了。

  • STATE_DRAGGING = 1,正在拽托。

  • STATE_SETTLING = 2,fling操作時。

    • 提供Callback接口回調(diào)和設置方法。

    我們在onViewDragStateChanged()回調(diào)中判斷,狀態(tài)為STATE_IDLE,并且滑塊位置為終點值時,就為解鎖,并且回調(diào)Callback對象。

    public class SlideLockView extends FrameLayout { /** * 回調(diào) */ private Callback mCallback; /** * 是否解鎖 */ private boolean isUnlock = false; private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { private int mTop; //... @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); int lockBtnWidth = mLockBtn.getWidth(); //限制左右臨界點 int fullWidth = slideRail.getWidth(); //最多的右邊 int leftMaxDistance = fullWidth - getPaddingEnd() - lockBtnWidth; int left = mLockBtn.getLeft(); if (state == ViewDragHelper.STATE_IDLE) { //移動到最右邊,解鎖完成 if (left == leftMaxDistance) { //未解鎖才進行解鎖回調(diào),由于這個判斷會進兩次,所以做了標志位限制 if (!isUnlock) { isUnlock = true; if (mCallback != null) { mCallback.onUnlock(); } } } } } }); } public interface Callback { /** * 當解鎖時回調(diào) */ void onUnlock(); } public void setCallback(Callback callback) { mCallback = callback; }}

    完整代碼

    public class SlideLockView extends FrameLayout { /** * 滑動滑塊 */ private View mLockBtn; /** * 拽托幫助類 */ private ViewDragHelper mViewDragHelper; /** * 回調(diào) */ private Callback mCallback; /** * 是否解鎖 */ private boolean isUnlock = false; public SlideLockView(@NonNull Context context) { this(context, null); } public SlideLockView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SlideLockView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { final SlideLockView slideRail = this; mViewDragHelper = ViewDragHelper.create(this, 0.3f, new ViewDragHelper.Callback() { private int mTop; @Override public boolean tryCaptureView(@NonNull View child, int pointerId) { //判斷能拽托的View,這里會遍歷內(nèi)部子控件來決定是否可以拽托,我們只需要滑塊可以滑動 return child == mLockBtn; } @Override public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) { //拽托子View橫向滑動時回調(diào),回調(diào)的left,則是可以滑動的左上角x坐標 int lockBtnWidth = mLockBtn.getWidth(); //限制左右臨界點 int fullWidth = slideRail.getWidth(); //最少的左邊 int leftMinDistance = getPaddingStart(); //最多的右邊 int leftMaxDistance = fullWidth - getPaddingEnd() - lockBtnWidth; //修復兩端的臨界值 if (left < leftMinDistance) { return leftMinDistance; } else if (left > leftMaxDistance) { return leftMaxDistance; } return left; } @Override public int clampViewPositionVertical(@NonNull View child, int top, int dy) { //拽托子View縱向滑動時回調(diào),鎖定頂部padding距離即可,不能不復寫,否則少了頂部的padding,位置就偏去上面了 return getPaddingTop(); } @Override public void onViewCaptured(@NonNull View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); //捕獲到拽托的View時回調(diào),獲取頂部距離 mTop = capturedChild.getTop(); } @Override public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); //獲取滑塊當前的位置 int currentLeft = releasedChild.getLeft(); //獲取滑塊的寬度 int lockBtnWidth = mLockBtn.getWidth(); //獲取滑道寬度 int fullWidth = slideRail.getWidth(); //一般滑道的寬度,用來判斷滑塊距離起點近還是終點近 int halfWidth = fullWidth / 2; //松手位置在小于一半,并且滑動速度小于1000,則回到左邊 if (currentLeft <= halfWidth && xvel < 1000) { mViewDragHelper.settleCapturedViewAt(getPaddingStart(), mTop); } else { //否則去到右邊(寬度,減去padding和滑塊寬度) mViewDragHelper.settleCapturedViewAt(fullWidth - getPaddingEnd() - lockBtnWidth, mTop); } invalidate(); } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); int lockBtnWidth = mLockBtn.getWidth(); //限制左右臨界點 int fullWidth = slideRail.getWidth(); //最多的右邊 int leftMaxDistance = fullWidth - getPaddingEnd() - lockBtnWidth; int left = mLockBtn.getLeft(); if (state == ViewDragHelper.STATE_IDLE) { //移動到最右邊,解鎖完成 if (left == leftMaxDistance) { //未解鎖才進行解鎖回調(diào),由于這個判斷會進兩次,所以做了標志位限制 if (!isUnlock) { isUnlock = true; if (mCallback != null) { mCallback.onUnlock(); } } } } } }); } @Override protected void onFinishInflate() { super.onFinishInflate(); //找到需要拽托的滑塊 mLockBtn = findViewById(R.id.lock_btn); if (mLockBtn == null) { throw new NullPointerException("必須要有一個滑動滑塊"); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //將onInterceptTouchEvent委托給ViewDragHelper return mViewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //將onTouchEvent委托給ViewDragHelper mViewDragHelper.processTouchEvent(event); return true; } @Override public void computeScroll() { super.computeScroll(); //判斷是否移動到頭了,未到頭則繼續(xù) if (mViewDragHelper != null) { if (mViewDragHelper.continueSettling(true)) { invalidate(); } } } public interface Callback { /** * 當解鎖時回調(diào) */ void onUnlock(); } public void setCallback(Callback callback) { mCallback = callback; }}

    基本使用

    • Xml控件布局

    <?xml version="1.0" encoding="utf-8"?><FrameLayout 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:background="@drawable/app_lock_screen_bg" tools:context=".ScreenLockActivity"> <com.zh.android.slidelockscreen.widget.SlideLockView android:id="@+id/slide_rail" android:layout_width="300dp" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="20dp" android:background="@drawable/app_slide_rail_bg" android:paddingStart="6dp" android:paddingTop="8dp" android:paddingEnd="6dp" android:paddingBottom="8dp"> <com.zh.android.slidelockscreen.widget.ShineTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="20dp" android:gravity="center" android:text="右滑解鎖打開應用" android:textColor="@color/app_tip_text" android:textSize="18sp" /> <ImageView android:id="@id/lock_btn" android:layout_width="48dp" android:layout_height="48dp" android:src="@drawable/app_lock_btn" /> com.zh.android.slidelockscreen.widget.SlideLockView>FrameLayout>
    • Java代碼

    public class ScreenLockActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_screen_lock); SlideLockView slideRail = findViewById(R.id.slide_rail); slideRail.setCallback(new SlideLockView.Callback() { @Override public void onUnlock() { //解鎖,跳轉(zhuǎn)到首頁 Intent intent = new Intent(ScreenLockActivity.this, HomeActivity.class); startActivity(intent); finish(); } }); }}

    項目Github地址:

    (https://github.com/hezihaog/SlideLockScreen)

    到這里就結束了.

    往期精彩回顧:

    • Android自定義實現(xiàn)箭頭的繪制

    • Android自定義實現(xiàn)六邊形按鈕的繪制

    • Android使用Glide加載清晰長圖

    • Android開源圖表庫MpAndroidChart的使用

    • Android實現(xiàn)商城購物車功能

    總結

    以上是生活随笔為你收集整理的android搜索框功能实现_Android实现滑动解锁功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。