안 드 로 이 드 다양한 미끄럼 충돌 해결 방안
Android 에서 미끄럼 을 해결 하 는 방안 은 두 가지 가 있 습 니 다.외부 차단 법 과 내부 차단 법 입 니 다.
미끄럼 충돌 에 도 두 가지 장면 이 존재 한다.가로 세로 미끄럼 충돌,같은 방향 미끄럼 충돌 이다.
그래서 저 는 미끄럼 충돌 을 어떻게 해결 하 는 지 네 가지 예 를 들 었 습 니 다.이 네 가지 예 는 외부 차단 법 으로 가로 세로 충돌 을 해결 하고 외부 차단 법 으로 같은 방향 충돌 을 해결 하 며 내부 차단 법 으로 가로 세로 충돌 을 해결 하고 내부 차단 법 으로 같은 방향 충돌 을 해결 하 는 것 입 니 다.
선행 효과 도:
실전
1.외부 차단 법,가로 세로 충돌 해결
사고방식 은 부모 컨트롤의 onInterceptTouchEvent 방법 을 다시 쓴 다음 에 구체 적 인 수요 에 따라 부모 컨트롤의 이벤트 차단 여 부 를 결정 하 는 것 이다.트 루 로 돌아 가 는 것 을 막 으 면 false 로 돌아 가 는 것 을 막 지 않 습 니 다.부모 컨트롤 이 이 벤트 를 차단 하면 부모 컨트롤 의 onTouchEvent 에서 해당 하 는 이벤트 처 리 를 합 니 다.
나의 이 예 는 가로 로 미 끄 러 지 는 View Group 에 세로 로 미 끄 러 지 는 ListView 3 개가 포함 되 어 있다.다음은 제 가 코드 를 동봉 합 니 다.HorizontalEx.Java:
/**
* Created by blueberry on 2016/6/20.
*
*
*
*
*/
public class HorizontalEx extends ViewGroup {
private static final String TAG = "HorizontalEx";
private boolean isFirstTouch = true;
private int childIndex;
private int childCount;
private int lastXIntercept, lastYIntercept, lastX, lastY;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
public HorizontalEx(Context context) {
super(context);
init();
}
public HorizontalEx(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public HorizontalEx(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mScroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
childCount = getChildCount();
measureChildren(widthMeasureSpec, heightMeasureSpec);
if (childCount == 0) {
setMeasuredDimension(0, 0);
} else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(0).getMeasuredWidth();
height = getChildAt(0).getMeasuredHeight();
setMeasuredDimension(width, height);
} else if (widthMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(0).getMeasuredWidth();
setMeasuredDimension(width, height);
} else {
height = getChildAt(0).getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
child.layout(left + l, t, r + left, b);
left += child.getMeasuredWidth();
}
}
/**
*
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
/* Down , */
case MotionEvent.ACTION_DOWN:
lastXIntercept = x;
lastYIntercept = y;
intercepted = false;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_MOVE:
final int deltaX = x - lastXIntercept;
final int deltaY = y - lastYIntercept;
/* */
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
lastXIntercept = x;
lastYIntercept = y;
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
mVelocityTracker.addMovement(event);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
/* Down , ,
, lastX,lastY */
if (isFirstTouch) {
lastX = x;
lastY = y;
isFirstTouch = false;
}
final int deltaX = x - lastX;
scrollBy(-deltaX, 0);
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
final int childWidth = getChildAt(0).getWidth();
mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
float xVelocity = mVelocityTracker.getXVelocity();
if (Math.abs(xVelocity) > configuration.getScaledMinimumFlingVelocity()) {
childIndex = xVelocity < 0 ? childIndex + 1 : childIndex - 1;
} else {
childIndex = (scrollX + childWidth / 2) / childWidth;
}
childIndex = Math.min(getChildCount() - 1, Math.max(childIndex, 0));
smoothScrollBy(childIndex * childWidth - scrollX, 0);
mVelocityTracker.clear();
isFirstTouch = true;
break;
}
lastX = x;
lastY = y;
return true;
}
void smoothScrollBy(int dx, int dy) {
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, 500);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVelocityTracker.recycle();
}
}
호출 코드:
@Override
public void showOutHVData(List<String> data1, List<String> data2, List<String> data3) {
ListView listView1 = new ListView(getContext());
ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
listView1.setAdapter(adapter1);
ListView listView2 = new ListView(getContext());
ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
listView2.setAdapter(adapter2);
ListView listView3 = new ListView(getContext());
ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
listView3.setAdapter(adapter3);
ViewGroup.LayoutParams params
= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mHorizontalEx.addView(listView1, params);
mHorizontalEx.addView(listView2, params);
mHorizontalEx.addView(listView3, params);
}
사실 외부 차단 의 주요 사상 은 모두 onInterceptTouchEvent 에 대한 재 작성 에 있다.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
/* Down , */
case MotionEvent.ACTION_DOWN:
lastXIntercept = x;
lastYIntercept = y;
intercepted = false;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_MOVE:
final int deltaX = x - lastXIntercept;
final int deltaY = y - lastYIntercept;
/* */
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
lastXIntercept = x;
lastYIntercept = y;
return intercepted;
}
이것 은 거의 외부 차단 사건 을 실현 하 는 템 플 릿 입 니 다.여 기 는 ACTION 에 있 지 않 습 니 다.DOWN 에서 true 로 돌아 갑 니 다.그렇지 않 으 면 자 VIew 가 사건 을 얻 을 기회 가 없습니다.왜냐하면 ACTIONDOWN 에서 true 로 돌 아 왔 습 니 다.같은 이벤트 시퀀스 인 View Group 의 disPatchTouchEvent 는 onInterceptTouchEvent 방법 을 호출 하지 않 습 니 다.그리고 바로 ACTIONUP 에서 false 로 돌아 갑 니 다.부모 컨트롤 이 ACTION 을 차단 하면UP,그러면 하위 뷰 가 UP 이 벤트 를 얻 지 못 하면 하위 뷰 의 Onclick 방법 등에 영향 을 줄 수 있 습 니 다.그러나 이것 은 부모 컨트롤 에 영향 을 주지 않 습 니 다.부모 컨트롤 자 ACITON 이 라면MOVE 에서 사건 을 차단 하면 그들 UP 사건 도 반드시 처리 할 것 이다.왜냐하면 부모 컨트롤 이 사건 을 차단 하면 같은 사건 서열 의 모든 사건 을 그 에 게 맡 길 것 이다.이 결론 은 나의 이전 문장 에서 이미 분석 한 적 이 있다.
마지막 으로 ACTIONMOVE 에 서 는 수요 에 따라 차단 여 부 를 결정 한다.
2.내부 차단 법,가로 세로 충돌 해결
내부 차단 은 주로 부모 컨트롤 에 의존 하 는 requestDisallow Intercept TouchEvent 방법 입 니 다.이 방법 에 대해 제 이전 글 은 이미 분석 되 었 습 니 다.그 는 부모 컨트롤 의 플래그 를 설정 했다(FLAGDISALLOW_INTERCEPT)
이 플래그 는 부모 컨트롤 이 이 벤트 를 차단 할 지 여 부 를 결정 할 수 있 습 니 다.이 플래그 를 설정 하면 차단 하지 않 습 니 다.이 플래그 가 설정 되 어 있 지 않 으 면 부모 컨트롤 의 onInterceptTouchEvent()를 호출 하여 부모 컨트롤 이 차단 되 었 는 지 물 어 봅 니 다.이 플래그 는 다운 이벤트 에 유효 하지 않 습 니 다.
원본 코드 를 참고 할 수 있 습 니 다.
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
//
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
그럼 내부 차단 법 으로 사건 을 막 으 려 면첫 번 째 단계:
a.부모 컨트롤 의 onInterceptTouchEvent 를 다시 쓰 려 고 합 니 다.ACTIONDOWN 때 false 로 돌아 갑 니 다.담당 자 는 View 에서 requestDisallow InterceptTouchEvent 를 호출 하 는 것 도 어 쩔 수 없습니다.
b.그리고 다른 사건 이 있 으 면 모두 true 로 돌아 가 사건 을 차단 할 수 있 는 권 리 를 하위 View 에 맡 깁 니 다.
두 번 째 단계:
하위 View 의 dispatchTouchEvent 에서 부모 컨트롤 이 이 벤트 를 차단 할 지 여 부 를 결정 합 니 다.
a.먼저 Motion Event.ACTIONDOWN:mHorizontalEx2.requestDisallow InterceptTouchEvent(true)를 사용 합 니 다.책임 지면 다음 사건 이 오 면 부모 컨트롤 에 맡 깁 니 다.
b.그리고 Motion Event.ACTIONMOVE:업무 논리 에 따라 mHorizontalEx2.requestDisallowInterceptTouchEvent(false)를 호출 할 지 여 부 를 결정 합 니 다.부모 컨트롤 이 사건 을 차단 할 지 여 부 를 결정 합 니 다.
상위 코드 HorizontalEx2.java:
/**
* Created by blueberry on 2016/6/20.
*
* ListViewEx
*/
public class HorizontalEx2 extends ViewGroup {
private int lastX, lastY;
private int childIndex;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
public HorizontalEx2(Context context) {
super(context);
init();
}
public HorizontalEx2(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public HorizontalEx2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mScroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int childCount = getChildCount();
measureChildren(widthMeasureSpec, heightMeasureSpec);
if (childCount == 0) {
setMeasuredDimension(0, 0);
} else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
height = getChildAt(0).getMeasuredHeight();
width = childCount * getChildAt(0).getMeasuredWidth();
setMeasuredDimension(width, height);
} else if (widthMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(0).getMeasuredWidth();
setMeasuredDimension(width, height);
} else {
height = getChildAt(0).getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftOffset = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(l + leftOffset, t, r + leftOffset, b);
leftOffset += child.getMeasuredWidth();
}
}
/**
* Down ,
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
return true;
}
return false;
} else {
return true;
}
}
private boolean isFirstTouch = true;
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
mVelocityTracker.addMovement(event);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
if (isFirstTouch) {
isFirstTouch = false;
lastY = y;
lastX = x;
}
final int deltaX = x - lastX;
scrollBy(-deltaX, 0);
break;
case MotionEvent.ACTION_UP:
isFirstTouch = true;
int scrollX = getScrollX();
mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
float mVelocityX = mVelocityTracker.getXVelocity();
if (Math.abs(mVelocityX) > configuration.getScaledMinimumFlingVelocity()) {
childIndex = mVelocityX < 0 ? childIndex + 1 : childIndex - 1;
} else {
childIndex = (scrollX + getChildAt(0).getWidth() / 2) / getChildAt(0).getWidth();
}
childIndex = Math.min(getChildCount() - 1, Math.max(0, childIndex));
smoothScrollBy(childIndex*getChildAt(0).getWidth()-scrollX,0);
mVelocityTracker.clear();
break;
}
lastX = x;
lastY = y;
return true;
}
private void smoothScrollBy(int dx, int dy) {
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,500);
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVelocityTracker.recycle();
}
}
ListViewEx.java
/**
*
*/
public class ListViewEx extends ListView {
private int lastXIntercepted, lastYIntercepted;
private HorizontalEx2 mHorizontalEx2;
public ListViewEx(Context context) {
super(context);
}
public ListViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HorizontalEx2 getmHorizontalEx2() {
return mHorizontalEx2;
}
public void setmHorizontalEx2(HorizontalEx2 mHorizontalEx2) {
this.mHorizontalEx2 = mHorizontalEx2;
}
/**
* outter.requestDisallowInterceptTouchEvent();
*
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mHorizontalEx2.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
final int deltaX = x-lastYIntercepted;
final int deltaY = y-lastYIntercepted;
if(Math.abs(deltaX)>Math.abs(deltaY)){
mHorizontalEx2.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
}
lastXIntercepted = x;
lastYIntercepted = y;
return super.dispatchTouchEvent(ev);
}
}
호출 코드:
@Override
public void showInnerHVData(List<String> data1, List<String> data2, List<String> data3) {
ListViewEx listView1 = new ListViewEx(getContext());
ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
listView1.setAdapter(adapter1);
listView1.setmHorizontalEx2(mHorizontalEx2);
ListViewEx listView2 = new ListViewEx(getContext());
ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
listView2.setAdapter(adapter2);
listView2.setmHorizontalEx2(mHorizontalEx2);
ListViewEx listView3 = new ListViewEx(getContext());
ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
listView3.setAdapter(adapter3);
listView3.setmHorizontalEx2(mHorizontalEx2);
ViewGroup.LayoutParams params
= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mHorizontalEx2.addView(listView1, params);
mHorizontalEx2.addView(listView2, params);
mHorizontalEx2.addView(listView3, params);
}
이로써 두 가지 차단 방법 은 이미 공 부 를 마 쳤 으 며,다음은 같은 방향 으로 미 끄 러 지 는 충돌 을 어떻게 해결 하 는 지 배 워 보 자.사실 위의 두 가지 예 와 사고방식 은 같다.단지 차단 여 부 를 판단 하 는 그 논리 가 다 를 뿐이다.
아래 의 예 는 드 롭 다운 새로 고침 컨트롤 입 니 다.
3.외부 차단 과 같은 방향 미끄럼 충돌 해결
RefreshLayoutBase.java
package com.blueberry.sample.widget.refresh;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;
import com.blueberry.sample.R;
/**
* ( )
*
*/
public abstract class RefreshLayoutBase<T extends View> extends ViewGroup {
private static final String TAG = "RefreshLayoutBase";
public static final int STATUS_LOADING = 1;
public static final int STATUS_RELEASE_TO_REFRESH = 2;
public static final int STATUS_PULL_TO_REFRESH = 3;
public static final int STATUS_IDLE = 4;
public static final int STATUS_LOAD_MORE =5;
private static int SCROLL_DURATION =500;
protected ViewGroup mHeadView;
protected ViewGroup mFootView;
private T contentView;
private ProgressBar headProgressBar;
private TextView headTv;
private ProgressBar footProgressBar;
private TextView footTv;
private boolean isFistTouch = true;
protected int currentStatus = STATUS_IDLE;
private int mScreenWidth;
private int mScreenHeight;
private int mLastXIntercepted;
private int mLastYIntercepted;
private int mLastX;
private int mLastY;
protected int mInitScrollY = 0;
private int mTouchSlop;
protected Scroller mScoller;
private OnRefreshListener mOnRefreshListener;
public RefreshLayoutBase(Context context) {
this(context, null);
}
public RefreshLayoutBase(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshLayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getScreenSize();
initView();
mScoller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setPadding(0, 0, 0, 0);
}
public void setContentView(T view) {
addView(view, 1);
}
public OnRefreshListener getOnRefreshListener() {
return mOnRefreshListener;
}
public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
this.mOnRefreshListener = mOnRefreshListener;
}
private void initView() {
setupHeadView();
setupFootView();
}
private void getScreenSize() {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScreenHeight = metrics.heightPixels;
}
private int dp2px(int dp) {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
}
/**
*
*/
private void setupHeadView() {
mHeadView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_head_view, null);
mHeadView.setBackgroundColor(Color.RED);
headProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressbar);
headTv = (TextView) mHeadView.findViewById(R.id.head_tv);
/* 1/4 , 100dp*/
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, mScreenHeight / 4);
mHeadView.setLayoutParams(layoutParams);
mHeadView.setPadding(0, mScreenHeight / 4 - dp2px(100), 0, 0);
addView(mHeadView);
}
/**
*
*/
private void setupFootView() {
mFootView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_foot_view, null);
mFootView.setBackgroundColor(Color.BLUE);
footProgressBar = (ProgressBar) mFootView.findViewById(R.id.fresh_foot_progressbar);
footTv = (TextView) mFootView.findViewById(R.id.fresh_foot_tv);
addView(mFootView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int finalHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
finalHeight += child.getMeasuredHeight();
}
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(0).getMeasuredWidth();
setMeasuredDimension(widthSize, finalHeight);
} else if (widthMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(0).getMeasuredWidth();
setMeasuredDimension(widthSize, height);
} else {
setMeasuredDimension(widthSize, finalHeight);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int topOffset = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
topOffset += child.getMeasuredHeight();
}
mInitScrollY = mHeadView.getMeasuredHeight() + getPaddingTop();
scrollTo(0, mInitScrollY);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastXIntercepted = x;
mLastYIntercepted = y;
break;
case MotionEvent.ACTION_MOVE:
final int deltaY = x - mLastYIntercepted;
if (isTop() && deltaY > 0 && Math.abs(deltaY) > mTouchSlop) {
/* */
intercepted = true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
mLastXIntercepted = x;
mLastYIntercepted = y;
return intercepted;
}
private void doRefresh() {
Log.i(TAG, "doRefresh: ");
if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
currentStatus = STATUS_IDLE;
} else if (currentStatus == STATUS_PULL_TO_REFRESH) {
mScoller.startScroll(0,getScrollY(),0,0-getScrollY(),SCROLL_DURATION);
if (null != mOnRefreshListener) {
currentStatus = STATUS_LOADING;
mOnRefreshListener.refresh();
}
}
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScoller.isFinished()) {
mScoller.abortAnimation();
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
if (isFistTouch) {
isFistTouch = false;
mLastX = x;
mLastY = y;
}
final int deltaY = y - mLastY;
if (currentStatus != STATUS_LOADING) {
changeScrollY(deltaY);
}
break;
case MotionEvent.ACTION_UP:
isFistTouch = true;
doRefresh();
break;
}
mLastX = x;
mLastY = y;
return true;
}
private void changeScrollY(int deltaY) {
Log.i(TAG, "changeScrollY: ");
int curY = getScrollY();
if (deltaY > 0) {
/* */
if (curY - deltaY > getPaddingTop()) {
scrollBy(0, -deltaY);
}
} else {
/* */
if (curY - deltaY <= mInitScrollY) {
scrollBy(0, -deltaY);
}
}
curY = getScrollY();
int slop = mInitScrollY / 2;
if (curY > 0 && curY <=slop) {
currentStatus = STATUS_PULL_TO_REFRESH;
} else if (curY > 0 && curY >= slop) {
currentStatus = STATUS_RELEASE_TO_REFRESH;
}
}
@Override
public void computeScroll() {
if (mScoller.computeScrollOffset()) {
scrollTo(mScoller.getCurrX(), mScoller.getCurrY());
postInvalidate();
}
}
/**
*
*/
public void refreshComplete() {
mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
currentStatus = STATUS_IDLE;
invalidate();
}
/**
* Footer
*/
public void showFooter() {
if(currentStatus==STATUS_LOAD_MORE) return ;
currentStatus = STATUS_LOAD_MORE ;
mScoller.startScroll(0, getScrollY(), 0, mFootView.getMeasuredHeight()
, SCROLL_DURATION);
invalidate();
}
/**
* loadMore
*/
public void footerComplete() {
mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
invalidate();
currentStatus = STATUS_IDLE;
}
public interface OnRefreshListener {
void refresh();
}
abstract boolean isTop();
abstract boolean isBottom();
}
이것 은 추상 적 인 클래스 입 니 다.하위 클래스 를 만들어 서 isTop()과 isBottom()방법 을 계승 해 야 합 니 다.다음은 그것 의 실현 유형 을 보 여 줍 니 다.
package com.blueberry.sample.widget.refresh;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by blueberry on 2016/6/21.
*
* RefreshLayoutBase
*/
public class RefreshListView extends RefreshLayoutBase<ListView> {
private static final String TAG = "RefreshListView";
private ListView listView;
private OnLoadListener loadListener;
public RefreshListView(Context context) {
super(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ListView getListView() {
return listView;
}
public void setListView(final ListView listView) {
this.listView = listView;
setContentView(listView);
this.listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
/* bug: listView , footer
* , 。
* , listView onTouchEvent
* , 。
* */
if (currentStatus == STATUS_IDLE
&& getScrollY() <= mInitScrollY && isBottom()
) {
showFooter();
if (null != loadListener) {
loadListener.onLoadMore();
}
}
}
});
}
public OnLoadListener getLoadListener() {
return loadListener;
}
public void setLoadListener(OnLoadListener loadListener) {
this.loadListener = loadListener;
}
@Override
boolean isTop() {
return listView.getFirstVisiblePosition() == 0
&& getScrollY() <= mHeadView.getMeasuredHeight();
}
@Override
boolean isBottom() {
return listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1;
}
public interface OnLoadListener {
void onLoadMore();
}
}
4.내부 차단 법 은 같은 방향 으로 미 끄 러 지 는 것 을 해결한다.마찬가지 로 드 롭 다운 리 셋 구성 요소 입 니 다.실현 원리 가 같 기 때문에 편 하 게 쓰 입 니 다.미끄럼 충돌 이 해결 되면
RefreshLayoutBase2.java
package com.blueberry.sample.widget.refresh;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Scroller;
import com.blueberry.sample.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by blueberry on 2016/6/22.
* ListVieEx
* ,
*/
public class RefreshLayoutBase2 extends ViewGroup {
private static final String TAG = "RefreshLayoutBase2";
private static List<String> datas;
static {
datas = new ArrayList<>();
for (int i = 0; i < 40; i++) {
datas.add(" ―" + i);
}
}
private ViewGroup headView;
private ListViewEx lv;
private int lastY;
public int mInitScrollY;
private Scroller mScroller;
public RefreshLayoutBase2(Context context) {
this(context, null);
}
public RefreshLayoutBase2(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshLayoutBase2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScroller = new Scroller(context);
setupHeadView(context);
setupContentView(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int finalHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
finalHeight += child.getMeasuredHeight();
}
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(0).getMeasuredWidth();
setMeasuredDimension(widthSize, finalHeight);
} else if (widthMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(0).getMeasuredWidth();
setMeasuredDimension(widthSize, height);
} else {
setMeasuredDimension(widthSize, finalHeight);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int topOffset = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
topOffset += child.getMeasuredHeight();
}
mInitScrollY = headView.getMeasuredHeight() + getPaddingTop();
scrollTo(0, mInitScrollY);
}
/**
* Down
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) return false;
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
final int deltaY = y-lastY;
Log.i(TAG, "onTouchEvent: deltaY: "+deltaY);
if (deltaY >= 0 && lv.isTop() && getScrollY() - deltaY >=getPaddingTop()) {
scrollBy(0, -deltaY);
}
break;
case MotionEvent.ACTION_UP:
this.postDelayed(new Runnable() {
@Override
public void run() {
mScroller.startScroll(0,getScrollY(),0,mInitScrollY-getScrollY());
invalidate();
}
},2000);
break;
}
lastY = y ;
return true;
}
private void setupHeadView(Context context) {
headView = (ViewGroup) View.inflate(context, R.layout.fresh_head_view, null);
headView.setBackgroundColor(Color.RED);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);
addView(headView, params);
}
public void setupContentView(Context context) {
lv = new ListViewEx(context, this);
lv.setBackgroundColor(Color.BLUE);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, datas);
lv.setAdapter(adapter);
addView(lv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
public static class ListViewEx extends ListView {
private RefreshLayoutBase2 outter;
public ListViewEx(Context context, RefreshLayoutBase2 outter) {
super(context);
this.outter = outter;
}
public ListViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* outter.requestDisallowInterceptTouchEvent();
*
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
outter.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if ( isTop() && outter.getScrollY() <= outter.mInitScrollY) {
outter.requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.dispatchTouchEvent(ev);
}
public boolean isTop() {
return getFirstVisiblePosition() ==0;
}
}
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.