commit
113de985db
@ -0,0 +1,93 @@ |
|||||||
|
package io.legado.app.ui.widget; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.res.TypedArray; |
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.Paint; |
||||||
|
import android.graphics.Path; |
||||||
|
import android.graphics.Rect; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
import io.legado.app.R; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
public class ArcView extends View { |
||||||
|
private int mWidth; |
||||||
|
private int mHeight; |
||||||
|
/** |
||||||
|
* 弧形高度 |
||||||
|
*/ |
||||||
|
private int mArcHeight; |
||||||
|
/** |
||||||
|
* 背景颜色 |
||||||
|
*/ |
||||||
|
private int mBgColor; |
||||||
|
private Paint mPaint; |
||||||
|
private boolean mDirectionTop; |
||||||
|
private Context mContext; |
||||||
|
|
||||||
|
public ArcView(Context context) { |
||||||
|
this(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public ArcView(Context context, @Nullable AttributeSet attrs) { |
||||||
|
this(context, attrs, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public ArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { |
||||||
|
super(context, attrs, defStyleAttr); |
||||||
|
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcView); |
||||||
|
mArcHeight = typedArray.getDimensionPixelSize(R.styleable.ArcView_arcHeight, 0); |
||||||
|
mBgColor = typedArray.getColor(R.styleable.ArcView_bgColor, Color.parseColor("#303F9F")); |
||||||
|
mDirectionTop = typedArray.getBoolean(R.styleable.ArcView_arcDirectionTop, false); |
||||||
|
mContext = context; |
||||||
|
mPaint = new Paint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDraw(Canvas canvas) { |
||||||
|
super.onDraw(canvas); |
||||||
|
|
||||||
|
mPaint.setStyle(Paint.Style.FILL); |
||||||
|
mPaint.setColor(mBgColor); |
||||||
|
|
||||||
|
if (mDirectionTop) { |
||||||
|
Rect rect = new Rect(0, mArcHeight, mWidth, mHeight); |
||||||
|
canvas.drawRect(rect, mPaint); |
||||||
|
|
||||||
|
|
||||||
|
Path path = new Path(); |
||||||
|
path.moveTo(0, mArcHeight); |
||||||
|
path.quadTo(mWidth / 2, 0, mWidth, mArcHeight); |
||||||
|
canvas.drawPath(path, mPaint); |
||||||
|
} else { |
||||||
|
Rect rect = new Rect(0, 0, mWidth, mHeight - mArcHeight); |
||||||
|
canvas.drawRect(rect, mPaint); |
||||||
|
|
||||||
|
|
||||||
|
Path path = new Path(); |
||||||
|
path.moveTo(0, mHeight - mArcHeight); |
||||||
|
path.quadTo(mWidth / 2, mHeight, mWidth, mHeight - mArcHeight); |
||||||
|
canvas.drawPath(path, mPaint); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
||||||
|
int widthSize = MeasureSpec.getSize(widthMeasureSpec); |
||||||
|
int widthMode = MeasureSpec.getMode(widthMeasureSpec); |
||||||
|
int heightSize = MeasureSpec.getSize(heightMeasureSpec); |
||||||
|
int heightMode = MeasureSpec.getMode(heightMeasureSpec); |
||||||
|
|
||||||
|
if (widthMode == MeasureSpec.EXACTLY) { |
||||||
|
mWidth = widthSize; |
||||||
|
} |
||||||
|
if (heightMode == MeasureSpec.EXACTLY) { |
||||||
|
mHeight = heightSize; |
||||||
|
} |
||||||
|
setMeasuredDimension(mWidth, mHeight); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
package io.legado.app.ui.widget; |
||||||
|
|
||||||
|
import android.annotation.SuppressLint; |
||||||
|
import android.content.Context; |
||||||
|
import android.text.Layout; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.MotionEvent; |
||||||
|
|
||||||
|
import androidx.appcompat.widget.AppCompatTextView; |
||||||
|
|
||||||
|
public class ScrollTextView extends AppCompatTextView { |
||||||
|
|
||||||
|
//滑动距离的最大边界
|
||||||
|
private int mOffsetHeight; |
||||||
|
|
||||||
|
//是否到顶或者到底的标志
|
||||||
|
private boolean mBottomFlag = false; |
||||||
|
|
||||||
|
|
||||||
|
public ScrollTextView(Context context) { |
||||||
|
super(context); |
||||||
|
} |
||||||
|
|
||||||
|
public ScrollTextView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
} |
||||||
|
|
||||||
|
public ScrollTextView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||||
|
super(context, attrs, defStyleAttr); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
||||||
|
initOffsetHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { |
||||||
|
super.onTextChanged(text, start, lengthBefore, lengthAfter); |
||||||
|
initOffsetHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initOffsetHeight() { |
||||||
|
int paddingTop; |
||||||
|
int paddingBottom; |
||||||
|
int mHeight; |
||||||
|
int mLayoutHeight; |
||||||
|
|
||||||
|
//获得内容面板
|
||||||
|
Layout mLayout = getLayout(); |
||||||
|
if (mLayout == null) return; |
||||||
|
//获得内容面板的高度
|
||||||
|
mLayoutHeight = mLayout.getHeight(); |
||||||
|
//获取上内边距
|
||||||
|
paddingTop = getTotalPaddingTop(); |
||||||
|
//获取下内边距
|
||||||
|
paddingBottom = getTotalPaddingBottom(); |
||||||
|
|
||||||
|
//获得控件的实际高度
|
||||||
|
mHeight = getMeasuredHeight(); |
||||||
|
|
||||||
|
//计算滑动距离的边界
|
||||||
|
mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight; |
||||||
|
if (mOffsetHeight <= 0) { |
||||||
|
scrollTo(0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean dispatchTouchEvent(MotionEvent event) { |
||||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) { |
||||||
|
//如果是新的按下事件,则对mBottomFlag重新初始化
|
||||||
|
mBottomFlag = mOffsetHeight <= 0; |
||||||
|
} |
||||||
|
//如果已经不要这次事件,则传出取消的信号,这里的作用不大
|
||||||
|
if (mBottomFlag) { |
||||||
|
event.setAction(MotionEvent.ACTION_CANCEL); |
||||||
|
} |
||||||
|
return super.dispatchTouchEvent(event); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility") |
||||||
|
@Override |
||||||
|
public boolean onTouchEvent(MotionEvent event) { |
||||||
|
boolean result = super.onTouchEvent(event); |
||||||
|
//如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次
|
||||||
|
if (!mBottomFlag) |
||||||
|
getParent().requestDisallowInterceptTouchEvent(true); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) { |
||||||
|
super.onScrollChanged(horiz, vert, oldHoriz, oldVert); |
||||||
|
if (vert == mOffsetHeight || vert == 0) { |
||||||
|
//这里触发父布局或祖父布局的滑动事件
|
||||||
|
getParent().requestDisallowInterceptTouchEvent(false); |
||||||
|
mBottomFlag = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<declare-styleable name="ArcView"> |
||||||
|
<attr name="arcHeight" format="dimension" /> |
||||||
|
<attr name="bgColor" format="color" /> |
||||||
|
<attr name="arcDirectionTop" format="boolean" /> |
||||||
|
</declare-styleable> |
||||||
|
|
||||||
|
</resources> |
Loading…
Reference in new issue