commit
						a54ce37738
					
				@ -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