自定義View:側滑選單動畫實現
側滑選單貌似從QQ5.0後就火了起來,道長感覺有些莫名其妙,但是想想產品經理那匱乏的想象力就知道原因了,大家都懂的~~~
這裡道長先用動畫實現一遍側滑選單,然後再用SlideMenu實現一遍。道長廢話就不說了,直接飛起……
一、佈局
1.主介面佈局
這裡道長重寫了LinearLayout類用來攔截點選事件。
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyLinearLayout(Context context) {
super(context);
}
private SlideMenu slideMenu;
public void setSlideMenu(SlideMenu slideMenu) {
this.slideMenu = slideMenu;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 如果slideMenu處於關閉,則攔截並消費掉
if (slideMenu.getDragState() == SlideMenu.DragState.Close) {
return true;// 攔截
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果slideMenu處於關閉,則消費掉
if (slideMenu.getDragState() == SlideMenu.DragState.Close) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// 則讓SlideMenu關閉掉
slideMenu.close();
}
return true;// 消費掉
}
return super.onTouchEvent(event);
}
}
主介面的佈局程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<com.yushan.slidingmenu.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dl_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include android:id="@+id/main_title"
layout="@layout/layout_title"/>
<!--主佈局-->
<TextView
android:id="@+id/tv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:gravity="center"
android:text="主介面"
android:textColor="#333"
android:textSize="20sp" />
</com.yushan.slidingmenu.MyLinearLayout>
2.側滑選單佈局
注意:這裡設定側滑選單的寬度即為側滑選單佈局的寬度
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#fff"
android:orientation="vertical">
<ListView
android:id="@+id/lv_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
</ListView>
</LinearLayout>
3.總佈局
<?xml version="1.0" encoding="utf-8"?>
<com.yushan.slidingmenu.SlideMenu xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/slideMenu"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/dl_secondary"
layout="@layout/layout_secondary" />
<include
android:id="@+id/dl_main"
layout="@layout/layout_main" />
</com.yushan.slidingmenu.SlideMenu>
二、SlideMenu實現
- 重寫構造方法
public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SlideMenu(Context context) {
super(context);
init();
}
private void init() {
// 浮點型別的計算器
floatEvaluator = new FloatEvaluator();
viewDragHelper = ViewDragHelper.create(this, callback);
}
- 獲取子控制元件並測量寬高
/**
* 方法功能:獲取子控制元件
*/
protected void onFinishInflate() {
super.onFinishInflate();
// 簡單的異常處理
if (getChildCount() != 2) {
throw new IllegalArgumentException("SlideMenu only support 2 children!");
}
menuView = getChildAt(0);
mainView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChild(menuView, widthMeasureSpec, heightMeasureSpec);
}
/**
* 一般是在當前的view執行完onMeasure方法之後呼叫,所以在該方法中肯定可以獲取到寬高
*
* @param w:
* @param h:
* @param oldw:
* @param oldh:
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// dragRange = (int) (getMeasuredWidth() * 0.8f);
dragRange = menuView.getMeasuredWidth();
}
- 重寫Callback類中的方法
這裡面備註的比較詳細了,道長就不一一細講了。
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* 方法功能:捕獲所有子控制元件
* @param child:
* @param pointerId:
* @return :
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mainView;
}
@Override
public int getViewHorizontalDragRange(View child) {
return dragRange;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == mainView) {
if (left < 0) {
left = 0;//限制左邊
}
if (left > dragRange) {
left = dragRange;//限制右邊
}
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if (changedView == menuView) {
//讓menuView固定住,不要動
menuView.layout(0, 0, menuView.getMeasuredWidth(), menuView.getMeasuredHeight());
//同時讓mainView伴隨移動
int newLeft = mainView.getLeft() + dx;
//對newLeft進行限制
if (newLeft < 0) newLeft = 0;//限制左邊
if (newLeft > dragRange) newLeft = dragRange;//限制右邊
mainView.layout(newLeft, 0, newLeft + mainView.getMeasuredWidth(), mainView.getMeasuredHeight());
}
//1.計算移動的百分比
float fraction = mainView.getLeft() * 1f / dragRange;
//2.根據移動的百分比去執行伴隨動畫
executeAnim(fraction);
//3.回撥監聽器的方法
if (fraction == 1f && mState != DragState.Open) {
//說明是開啟,應該回撥onOpen
mState = DragState.Open;
if (listener != null) {
listener.onOpen();
}
} else if (fraction == 0f && mState != DragState.Close) {
//說明是關閉,應該回撥onClose
mState = DragState.Close;
if (listener != null) {
listener.onClose();
}
}
//回撥onDragging
if (listener != null) {
listener.onDragging(fraction);
}
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (mainView.getLeft() < dragRange / 2) {
//在左半邊,
close();
} else {
//在右半邊
open();
}
//提高滑動的敏感度
if (xvel > 200) {
open();
} else if (xvel < -200) {
close();
}
}
};
- 關閉/開啟動畫
/**
* 方法功能:關閉動畫
*/
public void close() {
mState = DragState.Close;
viewDragHelper.smoothSlideViewTo(mainView, 0, mainView.getTop());
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);//重新整理整個ViewGroup
}
/**
* 方法功能:開啟動畫
*/
public void open() {
mState = DragState.Open;
viewDragHelper.smoothSlideViewTo(mainView, dragRange, mainView.getTop());
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);//重新整理整個ViewGroup
}
- 滑動過程中的動畫
這個看自己具體的需求自己選擇……
private void executeAnim(float fraction) {
//fraction:0 - 1
//先縮放mainView
// float scaleVaule = 0.8f+(1-fraction)*0.2f;//1-0.8f
// ViewHelper.setScaleX(mainView, floatEvaluator.evaluate(fraction, 1f, 0.8f));
// ViewHelper.setScaleY(mainView, floatEvaluator.evaluate(fraction, 1f, 0.8f));
//平移menuView
ViewHelper.setTranslationX(menuView, floatEvaluator.evaluate(fraction, -menuView.getMeasuredWidth() / 2, 0));
//縮放menuView
// ViewHelper.setScaleX(menuView, floatEvaluator.evaluate(fraction, 1f, 0.5f));
// ViewHelper.setScaleY(menuView, floatEvaluator.evaluate(fraction, 0.5f, 1f));
//透明menuView
// ViewHelper.setAlpha(menuView, floatEvaluator.evaluate(fraction, 0.3f, 1f));
//給背景圖片新增顏色的遮罩效果
// getBackground().setColorFilter((Integer) ColorUtil.evaluateColor(fraction, Color.BLACK, Color.TRANSPARENT),
// PorterDuff.Mode.SRC_OVER);
// getBackground().setColorFilter((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.YELLOW),Mode.SRC_OVER);
}
- 新增拖拽回撥監聽方法
private OnSlideStateChangeListener listener;
public void setOnSlideStateChangeListener(OnSlideStateChangeListener listener) {
this.listener = listener;
}
/**
* 拖拽狀態改變的監聽器
*
* @author Administrator
*/
public interface OnSlideStateChangeListener {
void onOpen();
void onClose();
void onDragging(float fraction);
}
- 新增資料,實現回撥方法
private void initData(){
menuData = new ArrayList<>();
for (int i = 0; i < 50; i++){
String menuStr = "第"+ (i + 1) +"個條目";
menuData.add(menuStr);
}
adapter = new MenuListAdapter(this,menuData);
lv_menu.setAdapter(adapter);
}
private void initView(){
dl_main = (MyLinearLayout) findViewById(R.id.dl_main);
slideMenu = (SlideMenu) findViewById(R.id.slideMenu);
dl_main.setSlideMenu(slideMenu);
slideMenu.setOnSlideStateChangeListener(new SlideMenu.OnSlideStateChangeListener() {
@Override
public void onOpen() {
}
@Override
public void onClose() {
// 藉助NineOldAndroid的VIewPropertyAnimator
ViewPropertyAnimator.animate(dl_main)
.translationX(5)
.setInterpolator(new CycleInterpolator(4))
.setDuration(500)
.start();
}
@Override
public void onDragging(float fraction) {
ViewHelper.setAlpha(dl_main, 1 - fraction/2);
}
});
lv_menu = (ListView)findViewById(R.id.lv_menu);
lv_menu.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String str = menuData.get(position);
Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();
}
});
}
到這裡就完了,希望這篇部落格可以給你提供一些幫助。乾巴巴的,連效果圖都沒有,如果有需要可以自己去github上下載Demo。
原始碼下載
相關文章
- 自定義View:側滑選單實現View
- 自定義view——仿酷狗的側滑選單View
- 自定義ViewGroup,實現Android的側滑選單ViewAndroid
- 自定義RecyclerView實現側滑刪除View
- Android自定義View(四)側滑佈局AndroidView
- 自定義控制元件?試試300行程式碼實現QQ側滑選單控制元件行程
- android的左右側滑選單實現Android
- css3實現側邊滑動選單CSSS3
- 利用DrawerLayout實現側滑選單學習總結
- 自定義View:畫布實現自定義View(折線圖的實現)View
- (有圖)仿QQ側滑選單:RecyclerView側滑選單,長按拖拽,滑動刪除View
- layui自定義ajax左側三級選單UI
- 原生Android 側滑選單實踐(部分)Android
- 自定義View:自定義屬性(自定義按鈕實現)View
- flutter-簡單實現找妹子自定義viewFlutterView
- 屬性動畫:如何自定義View動畫View
- Flutter自定義View的實現FlutterView
- 自定義view實現半圓環View
- 【朝花夕拾】Android自定義View篇之(十一)View的滑動,彈性滑動與自定義PagerViewAndroidView
- Android側滑選單DrawerLayout使用Android
- Swift - 仿寫QQ側滑選單Swift
- Android 自定義View 滑動解鎖AndroidView
- 直播平臺搭建,自定義View實現loading動畫載入View動畫
- Android自定義View播放Gif動畫AndroidView動畫
- Android 自定義View之下雨動畫AndroidView動畫
- 自定義通過PopupWindow實現通用選單
- 短視訊平臺開發,依靠DrawerLayout實現側滑選單效果
- android利用RecyclerView+自定義View實現城市選擇介面AndroidView
- 【Android初級】如何實現一個有動畫效果的自定義下拉選單Android動畫
- Android 自定義View:屬性動畫(六)AndroidView動畫
- Flutter 側滑欄及城市選擇UI的實現FlutterUI
- 自定義view實現圓角圖片View
- Flutter:手把手教你實現一個仿QQ側滑選單的功能Flutter
- 簡單介紹Android自定義View實現時鐘功能AndroidView
- 實現 UITableViewCell 側滑操作列表UIView
- android 滑動刪除的listview(自定義view)AndroidView
- Android自定義View——從零開始實現可展開收起的水平選單欄AndroidView
- 自定義view 之多個引導層動畫效果View動畫