393 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.mixiaoxiao.library.splitlayout;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.hmi.autotest.R;
/**
* SplitLayout github/Mixiaoxiao
*
* @author Mixiaoxiao 2016/08/18
*/
public class SplitLayout extends ViewGroup {
static final String TAG = "SplitLayout";
static final boolean DEBUG = true;
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private static final float INVAID_SPLITPOSITION = Float.MIN_VALUE;
private static final int DEFAULT_SPLIT_HANDLE_SIZE_DP = 16;
private static final int DEFAULT_CHILD_MIN_SIZE_DP = 32;
private static final int[] PRESSED_STATE_SET = { android.R.attr.state_pressed };
private static final int[] EMPTY_STATE_SET = {};
private int mOrientation;
private float mSplitFraction;
private float mSplitPosition = INVAID_SPLITPOSITION;
private Drawable mHandleDrawable;
private int mHandleSize;
private boolean mHandleHapticFeedback;
private View mChild0, mChild1;
private float mLastMotionX, mLastMotionY;
private int mChildMinSize;
private int mWidth, mHeight;
private boolean mIsDragging = false;
private Bitmap[] cachedBitmapArray = new Bitmap[2];
private Bitmap cachedBitmap;
private Canvas cachedCanvas; // 使用双缓冲方式绘制拖动中的View变化提高效率
private Paint mPaint = new Paint();
private int dragForgroundColor = Color.argb(0.88f, 1f, 1f, 1f);
public SplitLayout(Context context) {
this(context, null, 0);
}
public SplitLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SplitLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SplitLayout, defStyleAttr, 0);
mOrientation = a.getInteger(R.styleable.SplitLayout_splitOrientation, HORIZONTAL);
mChildMinSize = a.getDimensionPixelSize(R.styleable.SplitLayout_splitChildMinSize,
dp2px(DEFAULT_CHILD_MIN_SIZE_DP));
mSplitFraction = a.getFloat(R.styleable.SplitLayout_splitFraction, 0.5f);
checkSplitFraction();
mHandleDrawable = a.getDrawable(R.styleable.SplitLayout_splitHandleDrawable);
if (mHandleDrawable == null) {
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, new ColorDrawable(0x990288d1));
stateListDrawable.addState(new int[] {}, new ColorDrawable(Color.TRANSPARENT));
stateListDrawable.setEnterFadeDuration(150);
stateListDrawable.setExitFadeDuration(150);
mHandleDrawable = stateListDrawable;
}
mHandleDrawable.setCallback(this);
mHandleSize = Math.round(a.getDimension(R.styleable.SplitLayout_splitHandleSize, 0f));
if (mHandleSize <= 0) {
mHandleSize = mOrientation == HORIZONTAL ? mHandleDrawable.getIntrinsicWidth() : mHandleDrawable
.getIntrinsicHeight();
}
if (mHandleSize <= 0) {
mHandleSize = dp2px(DEFAULT_SPLIT_HANDLE_SIZE_DP);
}
mHandleHapticFeedback = a.getBoolean(R.styleable.SplitLayout_splitHandleHapticFeedback, false);
dragForgroundColor = a.getColor(R.styleable.SplitLayout_splitDragForgroundColor, dragForgroundColor);
a.recycle();
mPaint.setColor(dragForgroundColor);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
checkChildren();
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSize > 0 && heightSize > 0) {
mWidth = widthSize;
mHeight = heightSize;
setMeasuredDimension(widthSize, heightSize);
checkSplitPosition();
final int splitPosition = Math.round(mSplitPosition);
if (mOrientation == VERTICAL) {
mChild0.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY));
mChild1.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize - splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY));
} else {
mChild0.measure(MeasureSpec.makeMeasureSpec(splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));
mChild1.measure(
MeasureSpec.makeMeasureSpec(widthSize - splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));
}
// 初始化双缓冲对应的Bitmap和canvas
cachedBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
cachedCanvas = new Canvas(cachedBitmap);
} else {
throw new IllegalStateException("SplitLayout with or height must not be MeasureSpec.UNSPECIFIED");
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int w = r - l;
int h = b - t;
final int splitPosition = Math.round(mSplitPosition);
if (mOrientation == VERTICAL) {
mChild0.layout(0, 0, w, splitPosition - mHandleSize / 2);
mChild1.layout(0, splitPosition + mHandleSize / 2, w, h);
} else {
mChild0.layout(0, 0, splitPosition - mHandleSize / 2, h);
mChild1.layout(splitPosition + mHandleSize / 2, 0, w, h);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
float x = ev.getX();
float y = ev.getY();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if (isUnderSplitHandle(x, y)) {
if (mHandleHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
mHandleDrawable.setState(PRESSED_STATE_SET);
mIsDragging = true;
getParent().requestDisallowInterceptTouchEvent(true);
getBitmapFromChildView(); // 截屏当前两个子View的图片用于在拖动过程中的
invalidate();
} else {
mIsDragging = false;
}
mLastMotionX = x;
mLastMotionY = y;
break;
}
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
getParent().requestDisallowInterceptTouchEvent(true);
if (mOrientation == VERTICAL) {
float deltaY = y - mLastMotionY;
onlyUpdateSplitPosition(deltaY);
// updateSplitPositionWithDelta(deltaY);
postInvalidate(); // 使用子View的截图重绘界面
} else {
float deltaX = x - mLastMotionX;
onlyUpdateSplitPosition(deltaX);
// updateSplitPositionWithDelta(deltaX);
postInvalidate();
}
mLastMotionX = x;
mLastMotionY = y;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
mHandleDrawable.setState(EMPTY_STATE_SET);
if (mOrientation == VERTICAL) {
float deltaY = y - mLastMotionY;
updateSplitPositionWithDelta(deltaY);
} else {
float deltaX = x - mLastMotionX;
updateSplitPositionWithDelta(deltaX);
}
mLastMotionX = x;
mLastMotionY = y;
mIsDragging = false;
}
break;
}
return mIsDragging;
}
private boolean isUnderSplitHandle(float x, float y) {
if (mOrientation == VERTICAL) {
return y >= (mSplitPosition - mHandleSize / 2) && y <= (mSplitPosition + mHandleSize / 2);
} else {
return x >= (mSplitPosition - mHandleSize / 2) && x <= (mSplitPosition + mHandleSize / 2);
}
}
private void updateSplitPositionWithDelta(float delta) {
mSplitPosition = mSplitPosition + delta;
checkSplitPosition();
requestLayout();
}
private void onlyUpdateSplitPosition(float delta) {
mSplitPosition = mSplitPosition + delta;
checkSplitPosition();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mSplitPosition != INVAID_SPLITPOSITION && mHandleDrawable != null) {
final int splitPosition = Math.round(mSplitPosition);
if (mOrientation == VERTICAL) {
mHandleDrawable.setBounds(0, splitPosition - mHandleSize / 2, mWidth, splitPosition + mHandleSize / 2);
} else {
mHandleDrawable.setBounds(splitPosition - mHandleSize / 2, 0, splitPosition + mHandleSize / 2, mHeight);
}
mHandleDrawable.draw(canvas);
}
Log.d("SplitLayout", "dispatchDraw");
if (mIsDragging) { // 开始拖动
// 获取两个子View的截图Bitmap
cachedCanvas.drawColor(getContext().getColor(android.R.color.transparent), PorterDuff.Mode.CLEAR);
if (cachedBitmapArray != null) {
Rect child0Rect = new Rect();
Rect child1Rect = new Rect();
if (mOrientation == VERTICAL) {
child0Rect.set(0, 0, getWidth(), (int) (mSplitPosition - mHandleSize / 2));
child1Rect.set(0, (int) (mSplitPosition + mHandleSize / 2), getWidth(), getHeight());
} else {
child0Rect.set(0, 0, (int) (mSplitPosition - mHandleSize / 2), getHeight());
child1Rect.set((int) (mSplitPosition + mHandleSize / 2), 0, getWidth(), getHeight());
}
// cachedCanvas.drawBitmap(cachedBitmapArray[0], null, child0Rect, null);
cachedCanvas.drawRect(child0Rect, mPaint);
cachedCanvas.drawBitmap(((BitmapDrawable)getContext().getDrawable(R.drawable.icon_app)).getBitmap(),
child0Rect.centerX() - (getContext().getDrawable(R.drawable.icon_app).getBounds().width()/2),
child0Rect.centerY() - (getContext().getDrawable(R.drawable.icon_app).getBounds().height()/2), null);
// cachedCanvas.drawBitmap(cachedBitmapArray[1], null, child1Rect, null);
cachedCanvas.drawRect(child1Rect, mPaint);
cachedCanvas.drawBitmap(((BitmapDrawable)getContext().getDrawable(R.drawable.icon_app)).getBitmap(),
child1Rect.centerX() - (getContext().getDrawable(R.drawable.icon_app).getBounds().width()/2),
child1Rect.centerY() - (getContext().getDrawable(R.drawable.icon_app).getBounds().height()/2), null);
canvas.drawBitmap(cachedBitmap, 0, 0, null);
}
}
}
private void checkSplitFraction() {
if (mSplitFraction < 0) {
mSplitFraction = 0;
} else if (mSplitFraction > 1) {
mSplitFraction = 1;
}
}
private void checkSplitPosition() {
if (mOrientation == VERTICAL) {
if (mSplitPosition == INVAID_SPLITPOSITION) {
mSplitPosition = mHeight * mSplitFraction;
}
final int min = mChildMinSize + mHandleSize / 2;
if (mSplitPosition < min) {
mSplitPosition = min;
} else {
final int max = mHeight - mChildMinSize - mHandleSize / 2;
if (mSplitPosition > max) {
mSplitPosition = max;
}
}
} else {
if (mSplitPosition == INVAID_SPLITPOSITION) {
mSplitPosition = mWidth * mSplitFraction;
}
final int min = mChildMinSize + mHandleSize / 2;
if (mSplitPosition < min) {
mSplitPosition = min;
} else {
final int max = mWidth - mChildMinSize - mHandleSize / 2;
if (mSplitPosition > max) {
mSplitPosition = max;
}
}
}
}
private void checkChildren() {
if (getChildCount() == 2) {
mChild0 = getChildAt(0);
mChild1 = getChildAt(1);
} else {
throw new IllegalStateException("SplitLayout ChildCount must be 2.");
}
}
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
if (mHandleDrawable != null) {
mHandleDrawable.jumpToCurrentState();
}
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateSplitPositionWithDelta(0);
}
@Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mHandleDrawable;
}
private int dp2px(float dp) {
return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f);
}
// 获取当前两个子View的Bitmap截图用于在拖动过程中的实时绘制
private void getBitmapFromChildView() {
// 先回收已有的缓存图片
for (Bitmap bitmap: cachedBitmapArray) {
if (bitmap != null) {
bitmap.recycle();
}
}
if (mChild0!=null) {
cachedBitmapArray[0] = loadBitmapFromViewBySystem(mChild0);
}
if (mChild1!=null) {
cachedBitmapArray[1] = loadBitmapFromViewBySystem(mChild1);
}
}
/**
*此方法直接截取屏幕指定view区域的内容
* @param view 需要截取屏幕的图片view
* @return Bitmap
*/
public static Bitmap loadBitmapFromViewBySystem(View view) {
if (view == null) {
return null;
}
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
// 设置拖动时前景图片的颜色值
public void setDragForgroundColor(int dragForgroundColor) {
this.dragForgroundColor = dragForgroundColor;
mPaint.setColor(dragForgroundColor);
}
public int getDragForgroundColor() {
return dragForgroundColor;
}
}