編輯:關於Android編程
一個自定義ViewGroup的工具類,它提供了許多有用的方法和狀態允許用戶去拖拽和繪制子View在自定義ViewGroup中的軌跡和位置。
ViewDragHelper的創建;
ViewDragHelper可以使用靜態方法創建一個實例:
ViewDragHelper.create(ViewGroup forParent,int sensitiveity,ViewDragHelper.Callback cb)
在自定義ViewGroup中,ViewDragHelper可以幫助我們來分析手勢和處理拖動。
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
//處理觸摸事件
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
//返回true,
return true;
}
使用ViewDragHelper來動態移動自定義ViewGroup中的控件:
public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) // Animate the view child to the given (left, top) position. // 返回true 代表還沒有移動到指定的位置,需要刷新界面,繼續移動 // 返回false 就停止工作
ViewDragHelper.Callback集成了許多可覆寫的方法,所有移動的控制在ViewDragHelper.Callback裡面來實現。
是否可以捕捉ViewGroup中的子組件:
public boolean tryCaptureView(View child, int pointerId) {
//返回true,就代表著可對該子組件處理滑動事件。否則就不會處理。
return true;
//只對特定的組件捕捉 return speChild == child;
}
clampViewPositionHorizontal[Vertical]:
處理子組件在水平或者豎直方向的滑動限制,在這個方法內部做子組件的邊界處理,就是確保子組件不會滑過界。
例如在豎直方向進行滑動時,一般先獲取控件可滑動到的頂端Y值和底端Y值,再進行一個取值
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//手指觸摸移動時實時回調, top表示要到的y位置
int topBound = ...;
int bottomBound = ...;
return Math.min(Math.max(topBound, top), bottomBound);
}
onViewPositionChanged
當前拖動的子組件位置變化時調用的方法。一般在該方法裡調整其他子組件的位置。
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//changedView為當前位置發生改變的View,left,top分別為該View的left和top坐標
}
onViewReleased
當手指釋放的時候會調用的方法。在這個方法裡實現松開時的滑動效果。
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//released 為釋放的View,xvel,yvel分別為該手指離開時滑動和豎直滑動的速度
}
getViewVertical[Horizontal]DragRangeHorizontal
@Override
public int getViewVerticalDragRange(View child) {
//返回當前捕捉的child子組件的豎直滑動范圍
return ...;
}
……
自定義可下拉展示內容的的ViewGroup
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="一自定義viewgroupdragdownlayout">一、自定義ViewGroup:DragDownLayout
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
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 android.widget.LinearLayout;
/**
* Created by cxm on 2016/8/22.
*/
public class DragDownLayout extends ViewGroup {
private ViewDragHelper dragHelper;
//下拉的控件和內容控件
private View mDragbar, mContentView;
private int dragRange;
//對外的接口
private OnOpenListener mOnOpenListener;
private OnCloseListener mOnCloseListener;
private boolean isOpen;//內容是否打開著
public DragDownLayout(Context context) {
super(context);
init();
}
public DragDownLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragDownLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DragDownLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
dragHelper = ViewDragHelper.create(this, mCallback);
}
@Override
protected void onLayout(boolean b, int l, int t, int r, int bo) {
mDragbar.layout(0, 0, getWidth(), mDragbar.getMeasuredHeight());
mContentView.layout(0, -mContentView.getMeasuredHeight(), getWidth(), 0);
}
@Override
public boolean onInterceptHoverEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
dragHelper.cancel();
return false;
}
return dragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
dragHelper.processTouchEvent(event);
return true;
}
private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mDragbar;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mContentView.layout(0, top-mContentView.getHeight(), getWidth(), top );
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int topBound = getPaddingTop();
int bottomBound = getHeight() - mDragbar.getHeight();
return Math.min(Math.max(topBound, top), bottomBound);
}
@Override
public int getViewVerticalDragRange(View child) {
dragRange = mContentView.getHeight();
return dragRange;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//下拉的位置大於內容控件的1/2時,則向下滑動到底
if (mContentView.getBottom()>mContentView.getHeight()/2) {
smoothToBottom();
} else if (mContentView.getBottom()<=mContentView.getHeight()/2) {
smoothToTop();
}
invalidate();
}
};
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mDragbar = getChildAt(0);
mContentView = getChildAt(1);
LayoutParams contPar = mDragbar.getLayoutParams();
//父容器給內容組件指定大小
int heighContentSpec = MeasureSpec.makeMeasureSpec(contPar.height, MeasureSpec.EXACTLY);
//測量子組件,內容組件寬隨父布局
mDragbar.measure(widthMeasureSpec, heighContentSpec);
LayoutParams delPar = mContentView.getLayoutParams();
int heightDelSpec = MeasureSpec.makeMeasureSpec(delPar.height, MeasureSpec.EXACTLY);
mContentView.measure(widthMeasureSpec, heightDelSpec);
//父布局設置最終的寬和高,寬沿用自己測量自己的widthMeasureSpec,高度使用內容布局的高度
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), contPar.height+delPar.height);
}
private void smoothToTop() {
if (dragHelper.smoothSlideViewTo(mDragbar, getPaddingLeft(), getPaddingTop())) {
ViewCompat.postInvalidateOnAnimation(this);
isOpen = false;
if(mOnCloseListener!=null) mOnCloseListener.close();
}
}
private void smoothToBottom() {
if (dragHelper.smoothSlideViewTo(mDragbar, getPaddingLeft(), getHeight()-getPaddingBottom()-mDragbar.getHeight())) {
ViewCompat.postInvalidateOnAnimation(this);
isOpen = true;
if(mOnOpenListener!=null) mOnOpenListener.open();
}
}
@Override
public void computeScroll() {
super.computeScroll();
if (dragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
public boolean isOpen(){
return isOpen;
}
public void openContent(){
if(!isOpen) smoothToBottom();
}
public void closeContent(){
if(isOpen) smoothToTop();
}
public interface OnOpenListener{
void open();
}
public interface OnCloseListener{
void close();
}
public void setOnOpenListener(OnOpenListener mOnOpenListener) {
this.mOnOpenListener = mOnOpenListener;
}
public void setOnCloseListener(OnCloseListener mOnCloseListener) {
this.mOnCloseListener = mOnCloseListener;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 2) {
throw new IllegalStateException("Just contain two Views/ViewGroups ");
}
}
}
使用時要標明拖拽控件和內容控件的高度。
mDragDownLayout = (DragDownLayout) findViewById(R.id.myDragDownLayout);
mDragDownLayout.setOnOpenListener(new DragDownLayout.OnOpenListener() {
@Override
public void open() {
Toast.makeText(MainActivity.this,"open",Toast.LENGTH_SHORT).show();
}
});
DragDownLayout
Android實戰教程--第三十八話《自定義通知NotifiCation》
上一篇小案例,完成了一個普通的通知,點擊通知啟動了一個活動。但是那裡的通知沒有加入些“靓點”,這一篇就給它加入自定義的布局,完成自定義的通知。應用
Android自定義View之可隨時暫停、開啟的圓形下載進度條
這是一個一言不合就手撸一個自定義View的任性時代,因此最近一段時間一直在學習自定義View相關的知識,也看了很多與此相關的博客,有句話叫做不要重復造輪子,別人寫好的直接
android 跨應用程序廣播發送接受
廣播作為android的四大組件之一,適用的地方還是很多,多用來特定條件情況下的通知。例如,開機,鬧鈴,電池電量過低等等。但還可以自定義廣播,用來兩個應用程序的通知。曾經
Android 自定View實現仿QQ運動步數圓弧及動畫效果
在之前的Android超精准計步器開發-Dylan計步中的首頁用到了一個自定義控件,和QQ運動的界面有點類似,還有動畫效果,下面就來講一下這個View是如何繪制的。1.先