編輯:關於Android編程
簡介: 一般我們在自定義ViewGroup 的時候會通常都會用到onInterceptTouchEvent ,onTouchEvent 這些方法去進行距離的判斷然後利用scroller 去進行目標的移動,從而實現ViewGroup 的自定義。此方法不但判斷麻煩,而且邏輯復雜,不易操作,今天給大家要價講的這個工具ViewDragHelper 是谷歌IO大會上推出的觸摸輔助開發工具,極大的簡化了開發自定義VIewGroup 的難度。
1.廢話不多說 ,首先要初始化 ViewDragHelper 這個工具類:
final float density = getResources().getDisplayMetrics().density;
final float minVel = MIN_FLING_VELOCITY * density;
//指定好需要處理拖動的ViewGroup和回調 就可以開始使用了
mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper());
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
//設置minVelocity
mViewDragHelper.setMinVelocity(minVel);
2.初始化 ViewDragHelper.Callback 這個觸摸操作類:並初始化 要操作VIew的位置控制方法回掉類:
onViewPositionChanged()
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
mLeft = left;
mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f);
Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize);
invalidate();
}
3.接下來我們會操作要移動VIew 的滑動限制距離 方法:
/**
* 限制子View水平拖拉操作。默認不支持水平操作,重寫該方法提供新的水平坐標(根據提供的渴望的水平坐標)
* 不重寫就不會支持水平坐標變化
*
* @param child Child view being dragged
* @param left Attempted motion along the X axis
* @param dx Proposed change in position for left
* @return The new clamped position for left
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80);
final int rightBound =v_content.getPaddingRight();
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft());
return newLeft;
}
}
限制View活動的范圍後 ,但我們釋放手指的時候我們會操作另外一個方法:
/**
* 手指離開屏幕
* 後續View 的坐標處理
* 比如 滑到超過一半 直接滑到滿屏 又或者滑到不到一半的時候
* 還原坐標
*
* @param releasedChild
* @param xvel
* @param yvel
* */
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel);
//上拉
if (mLeftSize > 0.5) {
openCover();
} else {
closeCover();
}
invalidate();
}
這樣一個基本的VIew滑動類就基本實現了。
接下來我貼出一個完整的示例:
package com.example.administrator.myapplication.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.administrator.myapplication.R;
import com.example.administrator.myapplication.widget.BotomDragLayout;
import com.example.administrator.myapplication.widget.LeftScrollDeleteDragLayout;
/**
* 側滑刪除列表
* 2016/4/21
* gxj
*/
public class LeftScrollDeleteLayoutFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle("LeftScrollDeleteLayoutFragment");
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final LeftScrollDeleteDragLayout view = (LeftScrollDeleteDragLayout) inflater.inflate(R.layout.left_scroll_delete_layout, container, false);
View view_bg =view.findViewById(R.id.del);
view_bg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(view.STATE == BotomDragLayout.CLOSEING){
view.openCover();
}else{
view.closeCover();
Toast.makeText(getActivity(),"刪除",Toast.LENGTH_SHORT).show();
}
}
});
return view;
}
}
所需要的xml布局:
完整的自定義側滑類代碼:
package com.example.administrator.myapplication.widget;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
*列表側滑刪除效果
* author gxj
* date 2016.5.22
*/
public class LeftScrollDeleteDragLayout extends ViewGroup {
private final String TAG = this.getClass().getSimpleName();
private ViewDragHelper mViewDragHelper;
/**
* Minimum velocity that will be detected as a fling
*/
private static final int MIN_FLING_VELOCITY = 400; // dips per second
/**
* 菜單欄的狀態
*/
public int STATE = 0;
public static final int CLOSEING = 0;
public static final int OPENED = 1;
private View v_content;
private int viewWidth = 0;
/**
* 滑動view 的頂部位置
*/
private int mLeft;
/**
* 滑動view 的滑動距離占自身高度的比例
*/
private float mLeftSize;
private View view_bg;
public LeftScrollDeleteDragLayout(Context context) {
this(context, null);
init();
}
public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init();
}
public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
v_content = findViewById(R.id.view_content);
view_bg = findViewById(R.id.view_bg);
viewWidth = v_content.getHeight();
}
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
postInvalidateOnAnimation();
}
}
public void openCover() {
STATE = LeftScrollDeleteDragLayout.OPENED;
mViewDragHelper.smoothSlideViewTo(v_content, -dip2px(getContext(),80), 0);
postInvalidateOnAnimation();
}
public void closeCover() {
STATE = LeftScrollDeleteDragLayout.CLOSEING;
mViewDragHelper.smoothSlideViewTo(v_content, 0, 0);
postInvalidateOnAnimation();
}
private void init() {
final float density = getResources().getDisplayMetrics().density;
final float minVel = MIN_FLING_VELOCITY * density;
//指定好需要處理拖動的ViewGroup和回調 就可以開始使用了
mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper());
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
//設置minVelocity
mViewDragHelper.setMinVelocity(minVel);
}
private class DefaultDragHelper extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View view, int i) {
return view == v_content;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
mLeft = left;
mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f);
Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize);
invalidate();
}
/**
* 當captureview被捕獲時回調
*
* @param capturedChild
* @param activePointerId
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
Log.d(TAG, "onViewCaptured()--:");
}
/**
* 手指離開屏幕
* 後續View 的坐標處理
* 比如 滑到超過一半 直接滑到滿屏 又或者滑到不到一半的時候
* 還原坐標
*
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel);
//上拉
if (mLeftSize > 0.5) {
openCover();
} else {
closeCover();
}
invalidate();
}
/**
* 當觸摸到邊緣的時候會調用
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags, pointerId);
Log.d(TAG, "onEdgeTouched()");
}
@Override
public boolean onEdgeLock(int edgeFlags) {
Log.d(TAG, "onEdgeLock()");
return super.onEdgeLock(edgeFlags);
}
/**
* 當觸摸到邊緣的時候會調用
*
* @param edgeFlags
* @param pointerId 可以指定觸摸邊緣的子View
*/
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
super.onEdgeDragStarted(edgeFlags, pointerId);
Log.d(TAG, "onEdgeDragStarted()");
mViewDragHelper.captureChildView(v_content, pointerId);
}
/**
* 返回當前移動的View 的position
*
* @param index
* @return
*/
@Override
public int getOrderedChildIndex(int index) {
return super.getOrderedChildIndex(index);
}
/**
* 限制水平移動范圍
* Return the magnitude of a draggable child view's horizontal range of motion in pixels.
* 似乎作用不大,其他情況只用於判斷是否可以拖動
* 具體返回值真正起作用在於{@link ViewDragHelper#smoothSlideViewTo(View, int, int)}
*
* @param child Child view to check
* @return range of horizontal motion in pixels
*/
@Override
public int getViewHorizontalDragRange(View child) {
return child ==v_content?dip2px(getContext(),80):0;
}
/**
* 限制子View水平拖拉操作。默認不支持水平操作,重寫該方法提供新的水平坐標(根據提供的渴望的水平坐標)
* 不重寫就不會支持水平坐標變化
*
* @param child Child view being dragged
* @param left Attempted motion along the X axis
* @param dx Proposed change in position for left
* @return The new clamped position for left
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80);
final int rightBound =v_content.getPaddingRight();
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft());
return newLeft;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mViewDragHelper.cancel();
return false;
}
//通過這個方法判斷是否攔截 滑動事件
boolean flag = mViewDragHelper.shouldInterceptTouchEvent(event);
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//通過這個方法判斷是否處理攔截的觸摸事件
mViewDragHelper.processTouchEvent(event);
return true;
}
/**
* 丈量所有控件的高度
* 可以得到每個控件的最終高度
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
viewWidth = v_content.getMeasuredWidth();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.e("onLayout", l + "|" + t + "|" + r + "|" + b);
v_content.layout(mLeft, t, mLeft+viewWidth,dip2px(getContext(),200) );
view_bg.layout(0, 0, r, dip2px(getContext(),200));
}
/**
* 根據手機的分辨率從 dp 的單位 轉成為 px(像素)
*/
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
DatePicker控件繼承自FrameLayout類,日期選擇控件的主要功能是向用戶提供包含年、月、日的日期數據並允許用戶對其修改。TimePicker控件繼承自Fra
Android如何獲取手機通話記錄,本文為大家揭曉。獲取手機通話記錄流程:1、 獲取ContentResolver; ContentResolver resolver =
今天我們研究一下如何在Android手機上顯示GIF動態圖片 首先需要在src目錄下新建一個自定義的View,代碼如下: import a
1、程序運行效果圖 二、代碼實現 1、main.xml 2、tab1.xml、tab2.xm