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