Android橫向滑動載入更多的控制元件的實現---HorizontalScrollSlideView
阿新 • • 發佈:2019-01-09
Android橫向滑動載入更多的控制元件的實現—HorizontalScrollSlideView
需求
之前公司業務要求做一個橫向滑動的,可以載入更多的控制元件,第一時間想到的就是 RecyclerView 來實現 ,後面仔細想想滑動攔截不好控制等等
所以就換了個思路來實現了。
思路:
控制元件繼承自LinearLayout,外面包裹一層HorizontalScrollView,並重寫dispatchTouchEvent()事件,當橫向滑動到LinearLayout的右邊緣滑動到控制元件的右邊緣時,將隱藏的側拉頭跟隨手勢慢慢拉出。這中間伴隨著側拉頭的狀態實時的改變。鬆手時,如果側拉的距離已經足夠多,則回撥 OnSlideBottomListener 。當回撥結束時,回彈底部箭頭,讓側拉頭再次隱藏。
大概的思路是這樣的:
先上效果圖:
先說件很操蛋的事情就是 HorizontalScrollView的滑動監聽事件是protected為了在外面能拿到這個滑動監聽,所以先把 這個控制元件重寫了把滑動事件先回調回來。
public class ObservableScrollView extends HorizontalScrollView {
private OnScrollChangedListener onScrollChangedListener;
public ObservableScrollView(Context context) {
super (context);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnScrollListener(OnScrollChangedListener onScrollChangedListener) {
this .onScrollChangedListener = onScrollChangedListener;
}
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (onScrollChangedListener != null) {
onScrollChangedListener.onScrollChanged(x, y, oldX, oldY);
}
}
public interface OnScrollChangedListener {
void onScrollChanged(int x, int y, int oldX, int oldY);
}
}
接下來便是我們的主要實現的了:
public class HorizontalScrollSlideView extends LinearLayout implements ObservableScrollView.OnScrollChangedListener {
private static final String TAG = "ScrollSlideView";
//移動觸發步幅
private final int MOVE_STRIDE = 6;
//記錄移動x
private float mRecodX;
//記錄偏移量
private float mOffsetX;
//底部分界線位置
private int mBottomParting;
//底部展示區長度
private int mBottomShow;
//底部觸發區長度
private int mBottomAll;
//是否有觸控
private boolean isDown = false;
private Handler mHandler;
//滑動觸發的監聽
private OnSlideBottomListener mOnSlideBottomListener;
//內容外部的滑動view
private ObservableScrollView mScroolView;
//包裹內容view
private LinearLayout mContentView;
//底部展示view
private View mBottomShowView;
//底部觸發到監聽的view
private View mBottomGoView;
private boolean needScrollBottom = true;
public HorizontalScrollSlideView(Context context) {
this(context, null);
}
public HorizontalScrollSlideView(Context context, AttributeSet attrs) {
super(context, attrs);
mHandler = new Handler();
//LayoutInflater.from(context).inflate(R.layout.horizontal_scroll_slide_view, this);
//LayoutInflater.from(context).inflate(R.layout.horizontal_scroll_slide_view_bottom, this);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mScroolView = new ObservableScrollView(context);
mContentView = new LinearLayout(context);
mScroolView.setLayoutParams(lp);
mContentView.setLayoutParams(new ViewGroup.LayoutParams(lp));
mScroolView.addView(mContentView);
mScroolView.setHorizontalScrollBarEnabled(false);
addView(mScroolView);
}
/**
* 設定滑動區的內容
*
* @param views
*/
public void setContentViews(List<View> views) {
mContentView.removeAllViews();
for (View view : views) {
mContentView.addView(view);
}
}
public void setContentView(View view) {
mContentView.removeAllViews();
mContentView.addView(view);
}
public ViewGroup getContentContainer() {
return mContentView;
}
/**
* 設定觸發goveiw的監聽
*
* @param listener
*/
public void setOnSlideBottomListener(OnSlideBottomListener listener) {
mOnSlideBottomListener = listener;
}
/**
* 覆蓋後,返回自定義底部view
*
* @return 底部展現view
*/
protected View getBottomShowView() {
TextView textView = new TextView(getContext());
textView.setText("繼續滑動\n檢視全部");
textView.setGravity(Gravity.CENTER);
textView.setClickable(false);
textView.setEnabled(false);
// textView.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
textView.setTextColor(getContext().getResources().getColor(R.color.gray_616161));
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(dp2px(100), ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(lp);
return textView;
}
/**
* 覆蓋後,返回自定義底部觸發view
*
* @return 底部觸發view
*/
protected View getBottomGoView() {
TextView textView = new TextView(getContext());
textView.setText("->");
textView.setGravity(Gravity.CENTER);
// textView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
textView.setTextColor(getContext().getResources().getColor(R.color.gray_616161));
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(dp2px(20), ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(lp);
return textView;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// mScroolView = findViewById(R.id.sv);
// mContentView = findViewById(R.id.content);
//mBottomShowView = findViewById(R.id.bottom_show);
//mBottomGoView = findViewById(R.id.bottom_go);
mScroolView.setOnScrollListener(this);
View showView = getBottomShowView();
if (showView != null) {
addView(showView);
mBottomShowView = showView;
}
View goView = getBottomGoView();
if (goView != null) {
addView(goView);
mBottomGoView = goView;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mBottomShow = mBottomShowView.getWidth();
mBottomAll = mBottomShow + mBottomGoView.getWidth();
mBottomParting = mBottomShow / 2;
// Log.i(TAG, "onmeassure: " + mBottomAll);
}
@Override
public void onScrollChanged(int x, int y, int oldX, int oldY) {
if (!isDown && x > oldX && isScrollBottom(true)) {
setScrollX(mBottomShow);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// Log.i(TAG, "dispatch: " + ev.getAction());
if (isScrollBottom(true) || getScrollX() > 0) {
handleTouch(ev);
} else {
mRecodX = ev.getX();
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
isDown = true;
} else if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
isDown = false;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//消費掉,保證dispatchTouchevent
if (needScrollBottom) {
ViewParent parent = this;
while (!((parent = parent.getParent()) instanceof ViewPager))
parent.requestDisallowInterceptTouchEvent(true);
}
return true;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isIntercept = isScrollContentBottom() && ev.getAction() == MotionEvent.ACTION_MOVE;
// Log.i(TAG, "onInterceptTouchEvent: " + ev.getAction() + " isINtercept:" + isIntercept);
if (isIntercept)
getParent().requestDisallowInterceptTouchEvent(true);
return isIntercept ? true : super.onInterceptTouchEvent(ev);
}
private boolean isScrollBottom(boolean isIncludeEqual) {
int sx = mScroolView.getScrollX();
int cwidth = mScroolView.getChildAt(0).getWidth();
int pwidth = getWidth();
// Log.i(TAG, "sx: " + sx + "cwidth: " + cwidth + "pwidth: " + pwidth);
if (needScrollBottom)
return isIncludeEqual ? sx + pwidth >= cwidth : sx + pwidth > cwidth;
else
return false;
}
public void setNeedScrollBottom(boolean needScrollBottom) {
this.needScrollBottom = needScrollBottom;
}
private boolean isScrollContentBottom() {
return getScrollX() > 0;
}
private boolean handleTouch(MotionEvent event) {
// Log.i(TAG, "handletouch: " + event.getAction());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mRecodX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
if (mRecodX == 0)
mRecodX = event.getX();
//移動的距離
mOffsetX = (event.getX() - mRecodX);
//是否達移動最小值
if (Math.abs(mOffsetX) < MOVE_STRIDE) {
return true;
}
//手指左滑
boolean isLeft = event.getX() - mRecodX < 0;
mRecodX = event.getX();
if (isLeft && getScrollX() >= mBottomAll) {
setScrollX(mBottomAll);
//Log.i(TAG,"1");
} else if (!isLeft && getScrollX() <= 0) {
setScrollX(0);
//Log.i(TAG,"2");
} else {
setScrollX((int) (getScrollX() - mOffsetX));
//Log.i(TAG,"3");
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (getScrollX() < mBottomParting) {
setScrollX(0);
} else {
int delay = 0;
if (getScrollX() >= mBottomAll - MOVE_STRIDE) {
Log.i(TAG, "slide bottom!");
if (mOnSlideBottomListener != null) {
mOnSlideBottomListener.onSlideBottom();
}
delay = 1000;
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
setScrollX(mBottomShow);
}
}, delay);
}
break;
}
return true;
}
int dp2px(int dp) {
return (int) (getContext().getResources().getDisplayMetrics().density * dp + 0.5f);
}
public interface OnSlideBottomListener {
void onSlideBottom();
}
}
使用起來也很簡單,就不單獨寫demo了:
horScrollView.setContentView(contanteView);
horScrollView.setOnSlideBottomListener(new HorizontalScrollSlideView.OnSlideBottomListener() {
@Override
public void onSlideBottom() {
//響應滑動檢視更多的事件
}
});