Android 自定义Title根据滑动距离动画效果

来源:互联网 发布:工业企业数据库 编辑:程序博客网 时间:2024/05/23 20:35

1. 预览图

图比较大,有6M左右,网速较慢的请耐心等待

image

2. 需求拆解

  1. 首先取得当前ScrollView的滑动距离
  2. 自定义TitleView,根据滚动距离展示不同的效果
  3. 标题栏分为3个部分:圆角背景、图标、文字
  4. 只有在最底部的时候,才显示图标,其他时候不显示图标
  5. 定义背景和文本的原始位置信息和目标位置信息,然后根据滚动距离动态计算实际的位置信息
  6. 在onDraw中根据实际位置信息,绘制背景和文字

3. 带滚动监听的ScrollView

低版本的ScrollView是没有类似onScrollChangeListener的监听器的,因为为了实现兼容,需要自己自定义一个ScrollView

/** * Created by chenchen on 2017/8/31. */public class RollCallbackScrollView extends ScrollView {    private OnScrollChangeListener onScrollChangeListener;    private int threshold = Utils.dip2px(getContext(), 150);    public RollCallbackScrollView(Context context) {        super(context);    }    public RollCallbackScrollView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public RollCallbackScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {        this.onScrollChangeListener = onScrollChangeListener;    }    @Override    protected void onScrollChanged(int l, int t, int oldl, int oldt) {        super.onScrollChanged(l, t, oldl, oldt);        if (onScrollChangeListener != null) {            onScrollChangeListener.onScrollChanged(l, t, oldl, oldt);        }    }    public interface OnScrollChangeListener {        void onScrollChanged(int l, int t, int oldl, int oldt);    }}

4. 自定义标题栏控件

因为代码比较简单,解释起来却比较复杂,我就直接在代码中注释,相信大家应该能看得懂

/** * Created by chenchen on 2017/9/4. */public class AnimTitleView extends View {    //背景的原始颜色和目标颜色    private static final int bgFromColor =Color.parseColor("#CC323744");    private static final int bgToColor =Color.parseColor("#FF2296F3");    //阈值,在这个值内渐变    private static final int Threshold = 300;    // 控件的宽高    private int width;    private int height;    private Paint paint;    //背景的原始位置、目标位置、当前位置    private RectF bgFromRect = new RectF();    private RectF bgToRect = new RectF();    private RectF bgRect = new RectF();    //图标的位置    private RectF iconRect = new RectF();    //文字的原始位置、目标位置、当前位置    private RectF textFromRect = new RectF();    private RectF textToRect = new RectF();    private RectF textRect = new RectF();    //图标与是否显示图标    private Bitmap icon;    private boolean showIcon;    //标题与字体大小、颜色    private String title;    private float textSize;    private int textColor;    //背景颜色和圆角    private int bgColor;    private int bgRound;    //外层ScrollView的scrollY    private int scrollY;    //左上边距、内补白、图标与文字的边距    private int leftMargin;    private int topMargin;    private int leftRightPadding;    private int topBottomPadding;    private int drawablePadding;    public AnimTitleView(Context context) {        super(context);        init();    }    public AnimTitleView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public AnimTitleView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init(){        paint = new Paint(Paint.ANTI_ALIAS_FLAG);        // 抗锯齿        paint.setAntiAlias(true);        paint.setFilterBitmap(true);        icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_shop_icon);        title = "";        textSize = Utils.dip2px(getContext(), 16);        bgRound = Utils.dip2px(getContext(), 45);        bgColor = bgFromColor;        textColor = Color.parseColor("#FFFFFF");        leftMargin = Utils.dip2px(getContext(), 10);        topMargin = Utils.dip2px(getContext(), 10);        leftRightPadding = Utils.dip2px(getContext(), 13);        topBottomPadding = Utils.dip2px(getContext(), 2);        drawablePadding = Utils.dip2px(getContext(), 8);    }    public void setTitle(String title) {        this.title = title;        invalidate();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        width = MeasureSpec.getSize(widthMeasureSpec);        height = MeasureSpec.getSize(heightMeasureSpec);        if(width != 0 && height != 0){            paint.setTextSize(textSize);            //格式化title,防止文字过长的异常情况,过长则截断加...            formatTitle(title, 1);            //得到文本的绘制宽度和高度            float textWidth = paint.measureText(title);            Paint.FontMetrics metrics = paint.getFontMetrics();            float textHeight = metrics.bottom - metrics.top;            //图标只有显示与不显示,位置信息只有一个            iconRect.set(leftMargin + leftRightPadding,                    (height - icon.getHeight()) / 2,                    leftMargin + leftRightPadding + icon.getWidth(),                    (height - icon.getHeight()) / 2 + icon.getHeight());            textFromRect.set(leftMargin + leftRightPadding + icon.getWidth() + drawablePadding,                    (height - textHeight) / 2,                    leftMargin + leftRightPadding + icon.getWidth() + drawablePadding + textWidth,                    (height - textHeight) / 2 + textHeight);            bgFromRect.set(leftMargin,                    Math.min(iconRect.top, textFromRect.top) - topBottomPadding,                    leftMargin + leftRightPadding + icon.getWidth() + drawablePadding + textWidth + leftRightPadding,                    Math.max(iconRect.bottom, textFromRect.bottom) + topBottomPadding);            textToRect.set((width - textWidth) / 2,                    (height - textHeight) / 2,                    (width - textWidth) / 2 + textWidth,                    (height - textHeight) / 2 + textHeight);            bgToRect.set(-bgRound, 0, width + bgRound, height);            //在测量后,需要立马调用一次,否则第一次进来不会draw            setScrollY(scrollY);        }        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onDraw(Canvas canvas) {        //绘制背景        paint.setColor(bgColor);        paint.setStyle(Paint.Style.FILL);        canvas.drawRoundRect(bgRect, bgRound, bgRound, paint);        //绘制icon        if(showIcon){            canvas.drawBitmap(icon, null, iconRect, paint);        }        //绘制文字        paint.setTextSize(textSize);        paint.setColor(textColor);        Paint.FontMetrics metrics = paint.getFontMetrics();        float drawX = (textRect.width() - paint.measureText(title)) / 2 + textRect.left;        float drawY = textRect.height() / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom + textRect.top;        canvas.drawText(title, drawX, drawY, paint);    }    /**     * 在外界的ScrollView的回调中调用     */    public void setScrollY(int scrollY){        //超出阈值,则直接采用阈值        if(scrollY > Threshold){            scrollY = Threshold;        }        this.scrollY = scrollY;        //得到比例        float scale = scrollY * 1.0f / Threshold;        bgRect.set(bgFromRect.left - (bgFromRect.left - bgToRect.left) * scale,                bgFromRect.top - (bgFromRect.top - bgToRect.top) * scale,                bgFromRect.right + (bgToRect.right - bgFromRect.right) * scale,                bgFromRect.bottom + (bgToRect.bottom - bgFromRect.bottom) * scale);        bgColor = evaluateColor(bgFromColor, bgToColor, scale);        showIcon = (scrollY == 0);        textRect.set(textFromRect.left - (textFromRect.left - textToRect.left) * scale,                textFromRect.top,                textFromRect.right + (textToRect.right - textFromRect.right) * scale,                textFromRect.bottom);        invalidate();    }    /**     * 文字过长,每次截断一个字符,然后加上...继续测量,递归调用直到满足要求     */    private void formatTitle(String text, int count){        float textWidth = paint.measureText(text);        if(textWidth >= width - leftMargin - leftRightPadding * 2 - icon.getWidth() - drawablePadding){            String temp = title.substring(0, title.length() - count) + "...";            formatTitle(temp, count + 1);        }else{            title = text;        }    }    /**     * 颜色渐变,需要把ARGB分别拆开进行渐变     */    private int evaluateColor(int startValue, int endValue, float fraction) {        if (fraction <= 0) {            return startValue;        }        if (fraction >= 1) {            return endValue;        }        int startInt = startValue;        int startA = (startInt >> 24) & 0xff;        int startR = (startInt >> 16) & 0xff;        int startG = (startInt >> 8) & 0xff;        int startB = startInt & 0xff;        int endInt = endValue;        int endA = (endInt >> 24) & 0xff;        int endR = (endInt >> 16) & 0xff;        int endG = (endInt >> 8) & 0xff;        int endB = endInt & 0xff;        return ((startA + (int) (fraction * (endA - startA))) << 24)                | ((startR + (int) (fraction * (endR - startR))) << 16)                | ((startG + (int) (fraction * (endG - startG))) << 8)                | ((startB + (int) (fraction * (endB - startB))));    }}

5. 依赖的工具方法

将dp转换为px

public static int dip2px(Context context, float dipValue) {    final float scale =context.getResources().getDisplayMetrics().density;    return (int) (dipValue * scale + 0.5f);}

6. 使用方式

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <wxf.love.cc.androiddemo.view.RollCallbackScrollView        android:id="@+id/scrollView"        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:background="#F4F6F9"            android:orientation="vertical">            <TextView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:text="测试测试测试测试测试测试测试测试测试测试测试测试测测试测试测试测试测试测试测试测试测试测试测试测试测试测试测测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试"                android:gravity="center"                android:textSize="40sp"                android:textColor="#FFFFFF"                />        </LinearLayout>    </wxf.love.cc.androiddemo.view.RollCallbackScrollView>    <wxf.love.cc.androiddemo.view.AnimTitleView        android:id="@+id/atv_title"        android:layout_width="match_parent"        android:layout_height="48dp" /></RelativeLayout>

MainActivity.java

public class MainActivity extends Activity {    private RollCallbackScrollView scrollView;    private AnimTitleView titleView;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        scrollView = (RollCallbackScrollView) findViewById(R.id.scrollView);        titleView = (AnimTitleView) findViewById(R.id.atv_title);        titleView.setTitle("测试标题");        scrollView.setOnScrollChangeListener(new RollCallbackScrollView.OnScrollChangeListener() {            @Override            public void onScrollChanged(int l, int t, int oldl, int oldt) {                titleView.setScrollY(t);            }        });    }}
阅读全文
0 0
原创粉丝点击