Android使用ViewDragHelper實(shí)現(xiàn)仿QQ6.0側(cè)滑界面(一)
QQ是大家離不開的聊天工具,方便既實(shí)用,自從qq更新至6.0之后,側(cè)滑由原來的劃出后主面板縮小變成了左右平滑,在外觀上有了很大的提升,于是我就是嘗試?yán)斫庀吕锩娴母鞣N邏輯,結(jié)合相關(guān)資料,研究研究。
知道這里面的一個(gè)主要類是ViewDragHelper,那么首先我們要先來了解一下這個(gè)ViewDragHelper類,正所謂打蛇打七寸,我們就先來看看官方文檔怎么介紹的,有什么奇特的功能。
首先繼承:
java.lang.Object
android.support.v4.widget.ViewDragHelper
直接父類是Object。
類概述
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
他是一個(gè)編寫自定義ViewGroup的工具類,本省提供了許多有用的方法和狀態(tài)允許用戶去拖拽和繪制他們?cè)诟竀iewGroup中的軌跡和位置。
Nested Classes(嵌套類)
ViewDragHelper.Callback
A Callback is used as a communication channel with the ViewDragHelper back to the parent view using it.
一個(gè)回調(diào)是用作ViewDragHelper和他的父view的通信的接口
一個(gè)公開靜態(tài)方法:
我們可以知道,ViewDragHelper是通過create()方法構(gòu)造出來。這個(gè)在后面會(huì)有詳細(xì)介紹。
讓我們?cè)趤砜聪滦枰玫降睦锩娴膸讉€(gè)方法:
public boolean tryCaptureView(View child, int pointerId) {}
public int getViewHorizontalDragRange(View child) {}
public int clampViewPositionHorizontal(View child, int left, int dx) {}
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}
public void onViewReleased(View releasedChild, float xvel, float yvel) {}
上面的幾個(gè)方法,會(huì)在代碼中有詳細(xì)的注釋,在這里只是看下我們需要重寫的方法
好了哈,說了這么多,我們就先來個(gè)簡(jiǎn)單的,就是可以實(shí)現(xiàn)拖拽(相信當(dāng)你的兩個(gè)view可以拖拽的時(shí)候,你會(huì)發(fā)現(xiàn),哦這么簡(jiǎn)單幾步么):
第一步實(shí)現(xiàn)拖拽功能(簡(jiǎn)單3步實(shí)現(xiàn))
//1、通過靜態(tài)方法初始化操作
mDragHelper = ViewDragHelper.create(this, mCallback);
/**
* 2、傳遞觸摸事件
*
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//讓自己的控件自行判斷是否去攔截
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
//自己去處理觸摸事件
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
//返回true,這樣才能持續(xù)接收,要不然我們不會(huì)傳遞而是被攔截了
return true;
}
ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
/**
* 根據(jù)返回結(jié)果決定當(dāng)前child是否可以拖拽
* 嘗試捕獲的時(shí)候調(diào)用,如果返回的是主面板,那么負(fù)面版是不能被調(diào)用的
* @param child 當(dāng)前被拖拽的view
* @param pointerId 區(qū)分多點(diǎn)觸摸的id
* @return 返回true 是都可以拖拽
* 返回child == mLeftContent 左側(cè)可以移動(dòng)
* 返回child == mMainContent 右側(cè)可以移動(dòng)
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
//這里要返回true,要不然不能拖拽
return true;
}
/**
* 根據(jù)建議值修正將要移動(dòng)的位置 此時(shí)并沒有發(fā)生真正的移動(dòng)(左右)
*
* @param child 當(dāng)前拖拽的view
* @param left 新的位置的建議值 oldLeft + dx
* @param dx 變化量 和變化之前位置的差值
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//返回的拖拽的范圍,不對(duì)拖拽進(jìn)行真正的限制,僅僅決定了動(dòng)畫執(zhí)行的速度
return left;
}
};
好了,第一個(gè)效果圖可以出來了,就是可以拖拽了,是不是 很簡(jiǎn)單的就實(shí)現(xiàn)了呢:
但是,你要是做成這樣提交任務(wù),是不是不想干活了哈,好了下面我們就來控制一下拖拽位置,不能讓他亂拖拽了哈。、
第二步,控制拖拽范圍
我們想要控制拖拽范圍,首先我們得需要拿到這兩個(gè)控件,取到有關(guān)這兩個(gè)控件的屬性,我們才能去操作。于是我們重寫了一下的方法:
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
* 當(dāng)xml填充完的時(shí)候去掉用,在這里我們可以找到我們要去操控的那兩個(gè)布局
* <p>Even if the subclass overrides onFinishInflate, they should always be
* sure to call the super method, so that we get called.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
/**
* 根據(jù)索引來找
*/
/**
* 得到左邊的布局
*/
mLeftContent = (ViewGroup) getChildAt(0);
/**
* 得到主main布局
*/
mMainContent = (ViewGroup) getChildAt(1);
}
接下來我們就要來得到有關(guān)寬和高了,我們知道onMessure()中可以獲取,不過查看了一下,下面的這個(gè)方法也是可以獲取到的:
/**
* 當(dāng)尺寸變化的時(shí)候去調(diào)用
* This is called during layout when the size of this view has changed
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
/**
* 屏幕的寬度和高度
*/
mHeight = getMeasuredHeight();
mWidth = getMeasuredWidth();
/**
* 自定義左側(cè)view拖拽出來的距離
*/
mRange = (int) (mWidth * 0.7);
}
好了,有關(guān)我們需要的寬、高、拖拽距離我們已經(jīng)獲取到了,那么我們接下來就要來限制了。
我們只需要在clampViewPositionHorizontal()方法中加入這個(gè),就能限制主面板不能往左移,左面板只能移動(dòng)到右側(cè)的mRange位置。(這里可以去嘗試一下哈)
if (child == mMainContent) {
left = fixLeft(left);
}
/**
* 修正方法
* 根據(jù)范圍去修正左側(cè)的view的可見
*
* @param left
* @return
*/
private int fixLeft(int left) {
if (left < 0) {
return 0;
} else if (left > mRange) {
return mRange;
}
return left;
}
這個(gè)時(shí)候,我們基本上的大致框架已經(jīng)出來了,但是還是有一個(gè)問題,那就是雖然我們達(dá)到了滑動(dòng)過的效果(右側(cè)的定了,但是當(dāng)我們把LeftView滑動(dòng)出來的時(shí)候,還是可以往右滑)我們要想辦法去限制,就是限制,左側(cè)的view我只能右滑mRange的距離這個(gè)時(shí)候我們就要查看方法了,哪個(gè)方法可以拿到變化的position的值,然后去改變:
/**
* 當(dāng)view位置改變的時(shí)候,處理要做的事情,更新狀態(tài),伴隨動(dòng)畫,重繪界面
*
* 此時(shí)view已經(jīng)發(fā)生了位置的改變
*
* @param changedView 改變的位置view
* @param left 新的左邊值
* @param top
* @param dx 水平變化量
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
int newLeft = left;
//如果我們拖拽的是左面板
if (changedView == mLeftContent) {
//新的左側(cè)位置是我們的主面板的左側(cè)加上水平變化量
newLeft = mMainContent.getLeft() + dx;
}
//進(jìn)行修正(不能超出我們的規(guī)定的范圍)
newLeft = fixLeft(newLeft);
if (changedView == mLeftContent) {
//當(dāng)左面板移動(dòng)之后,在強(qiáng)制放回去
mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
}
//兼容低版本 強(qiáng)制重繪
invalidate();
}
好了,這個(gè)時(shí)候我們的大致效果已經(jīng)出來了,看下效果圖。(這里的中間出來的白到屬于錄制問題,親測(cè)沒問題)
到這里,大致的拖拽已經(jīng)可以實(shí)現(xiàn)了,當(dāng)然了哈,我的現(xiàn)在布局就是下面的簡(jiǎn)單的實(shí)現(xiàn)(要先加一些控件,這個(gè)自己在兩個(gè)LinearLayout中加入即可)
<?xml version="1.0" encoding="utf-8"?> <com.example.qqsliding.DragLayout 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" tools:context="com.example.qqsliding.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/sidebar_bg"> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#0CB8F6" android:gravity="center_vertical"> <ImageView android:layout_width="30dp" android:layout_height="30dp" android:layout_marginLeft="15dp" android:src="@mipmap/icon_avatar_white"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="Header"/> <ImageView android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:src="@mipmap/icon_search"/> </RelativeLayout> </LinearLayout> </com.example.qqsliding.DragLayout>
但是細(xì)心的可能會(huì)發(fā)現(xiàn),我們拖拽的時(shí)候,特別生硬,那是因?yàn)槲覀儧]有加上動(dòng)畫效果,只是生生的給拖拽出來了,具體的加入動(dòng)畫,定義回調(diào)效果,請(qǐng)通過本文學(xué)習(xí)Android滑動(dòng)優(yōu)化高仿QQ6.0側(cè)滑菜單(二),希望本文分享對(duì)大家有所幫助。
- android 通過向viewpage中添加listview來完成滑動(dòng)效果(類似于qq滑動(dòng)界面)
- Android QQ登錄界面繪制代碼
- Android仿QQ、微信聊天界面長(zhǎng)按提示框效果
- Android使用ViewDragHelper實(shí)現(xiàn)QQ6.X最新版本側(cè)滑界面效果實(shí)例代碼
- Android仿QQ空間動(dòng)態(tài)界面分享功能
- Android應(yīng)用中使用ViewPager實(shí)現(xiàn)類似QQ的界面切換效果
- Android實(shí)現(xiàn)QQ登錄界面遇到問題及解決方法
- Android QQ新用戶注冊(cè)界面繪制
- Android ListView自定義Adapter實(shí)現(xiàn)仿QQ界面
- Android小程序?qū)崿F(xiàn)簡(jiǎn)易QQ界面
相關(guān)文章
Android開發(fā)之Animations動(dòng)畫用法實(shí)例詳解
這篇文章主要介紹了Android開發(fā)之Animations動(dòng)畫用法,結(jié)合實(shí)例形式詳細(xì)分析了Animations動(dòng)畫的類型、組成、模式及對(duì)應(yīng)的使用技巧,需要的朋友可以參考下2016-02-02
Android ImageView Src 和Background 區(qū)別
這篇文章主要介紹了Android ImageView Src 和Background 區(qū)別的相關(guān)資料,需要的朋友可以參考下2016-09-09
Android編程實(shí)現(xiàn)圖片放大縮小功能ZoomControls控件用法實(shí)例
這篇文章主要介紹了Android編程實(shí)現(xiàn)圖片放大縮小功能ZoomControls控件用法,結(jié)合具體實(shí)例形式分析了Android ZoomControls控件實(shí)現(xiàn)圖片縮放的具體操作方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-09-09
Android Flutter實(shí)現(xiàn)視頻上滑翻頁(yè)效果的示例代碼
我們?cè)诙桃曨l應(yīng)用中經(jīng)常會(huì)看到不停上滑瀏覽下一條視頻的沉浸式交互效果,這種交互能夠讓用戶不停地翻頁(yè),直到找到喜歡的視頻內(nèi)容。本文將通過Flutter中的PageView組件實(shí)現(xiàn),感興趣的可以了解一下2022-10-10
Android編程實(shí)現(xiàn)將壓縮數(shù)據(jù)庫(kù)文件拷貝到安裝目錄的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)將壓縮數(shù)據(jù)庫(kù)文件拷貝到安裝目錄的方法,涉及Android處理壓縮文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
Android實(shí)現(xiàn)多級(jí)列表中的新建功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多級(jí)列表中的新建功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
Android中使用AsyncTask做下載進(jìn)度條實(shí)例代碼
這篇文章主要介紹了Android中使用AsyncTask做下載進(jìn)度條實(shí)例代碼的相關(guān)資料,這里附有實(shí)例代碼,具有一定參考價(jià)值,需要的朋友可以參考下2017-01-01
View觸發(fā)機(jī)制API實(shí)現(xiàn)GestureDetector OverScroller詳解
這篇文章主要為大家介紹了View觸發(fā)機(jī)制API實(shí)現(xiàn)GestureDetector OverScroller詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android 10 啟動(dòng)之servicemanager源碼解析
這篇文章主要為大家介紹了Android 10 啟動(dòng)之servicemanager源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

