汪建军的博客

休迅飞凫,飘忽若神,凌波微步,罗袜生尘。

Android developer, sometimes thinking, sometimes try it.


给RecyclerView添加水印

目标:

给RecyclerView添加重复文字水印,水印随RecyclerView的滑动而RecyclerView,效果如下图:

我哥们通过RecyclerView.ItemDecoration的方式实现,效果不错,这是链接。受其启发我也研究了一下。

第一步:生成重复水印图

通常有两种方法:

Bitmap形式:

参考Android官方文档,设计师P一块所需的图片,如下方法设置可平铺展示:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"  
    android:src="@mipmap/statistics_experience_bg"
    android:antialias="true"
    android:dither="true"
    android:filter="true"
    android:tileMode="repeat" />

Canvas形式:

也就是纯代码形式

    BitmapDrawable getWatermarkBitmapDrawable(Context context, String text) {
        int size = 320;
        int degrees = -30;

        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(Color.WHITE);
        Paint paint = new Paint();
        paint.setColor(Color.GRAY);
        paint.setAlpha(80);
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.LEFT);
        paint.setTextSize(40);

        // 适当旋转一定角度,稍往右画一点防止文字被截
        canvas.rotate(degrees, 0, size);
        canvas.drawText(text, -degrees * 2, size, paint);

        BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
        bitmapDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        bitmapDrawable.setDither(true);

        return bitmapDrawable;
    }

由于我们想水印文字可以自定义,所以用Canvas的形式。

第二步:包裹RecyclerView

把要有水印的RecyclerView用FrameLayout包裹起来

<FrameLayout  
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/view_watermark"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:listitem="@layout/item_watermark" />

</FrameLayout>  

这里的TextView就是我们的水印背景。为什么不直接用View而是TextView。 因为View的onMeasure十分简单,没有计算margin,而margin是我们后面要实现随Recyclerview滑动的基础,所以随便用一个TextView就可以了。

第三步:水印随RecyclerView滚动

添加RecyclerView的滚动事件

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                moveY += dy;
                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) viewWatermark.getLayoutParams();
                lp.topMargin = -moveY;
                viewWatermark.setLayoutParams(lp);
            }
        });

第四步:将RecyclerView的每一项layout的背景设置为透明

我们实现的思路就是让水印TextView在透明的RecyclerView之下。

封装

按照以上四步就可以实现基于RecyclerView的列表水印,但是我们要用起来比较优雅,适应各种情况,所以按我哥们提供的构建器思路做了如下封装:

/**
 * 水印图
 * Created by Wang on 2018/1/24.
 */
public class Watermark {

    private WatermarkParams mWatermarkParams;

    private Watermark(WatermarkParams mWatermarkParams) {
        this.mWatermarkParams = mWatermarkParams;
    }

    public BitmapDrawable getWatermarkBitmapDrawable() {

        Bitmap bitmap = Bitmap.createBitmap(mWatermarkParams.mWidth, mWatermarkParams.mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(mWatermarkParams.mBackgroundColor);
        Paint paint = new Paint();
        paint.setColor(mWatermarkParams.mTextColor);
        paint.setAlpha((int) (255 * mWatermarkParams.mAlpha));
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.LEFT);
        paint.setTextSize(mWatermarkParams.mTextSize);

        canvas.rotate(mWatermarkParams.mDegrees, 0, mWatermarkParams.mHeight);
        canvas.drawText(mWatermarkParams.mDrawText, -mWatermarkParams.mDegrees, mWatermarkParams.mHeight, paint);

        BitmapDrawable bitmapDrawable = new BitmapDrawable(mWatermarkParams.context.getResources(), bitmap);
        bitmapDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        bitmapDrawable.setDither(true);

        return bitmapDrawable;
    }

    public static class Builder {

        private WatermarkParams mWatermarkParams;

        //水印背景字符
        public Builder(Context context, String drawText) {
            mWatermarkParams = new WatermarkParams(context, drawText);
        }

        //文字大小sp
        public Builder setTextSizeSp(int textSize) {
            mWatermarkParams.mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP
                    , textSize, mWatermarkParams.context.getResources().getDisplayMetrics());
            return this;
        }

        //单项宽度
        public Builder setWidthDp(int width) {
            mWatermarkParams.mWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
                    , width, mWatermarkParams.context.getResources().getDisplayMetrics());
            return this;
        }

        //单项高度
        public Builder setHeightDp(int height) {
            mWatermarkParams.mHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
                    , height, mWatermarkParams.context.getResources().getDisplayMetrics());
            return this;
        }


        //文字颜色
        public Builder setTextColor(@ColorInt int textColor) {
            mWatermarkParams.mTextColor = textColor;
            return this;
        }

        //背景颜色
        public Builder setBackgroundColor(@ColorInt int backgroundColor) {
            mWatermarkParams.mBackgroundColor = backgroundColor;
            return this;
        }

        //倾斜角度
        public Builder setDegrees(int degrees) {
            mWatermarkParams.mDegrees = degrees;
            return this;
        }

        //透明度 0-1
        public Builder setAlpha(float alpha) {
            mWatermarkParams.mAlpha = alpha;
            return this;
        }

        public Watermark build() {
            return new Watermark(mWatermarkParams);
        }

    }

    private static class WatermarkParams {

        Context context;
        String mDrawText;
        int mBackgroundColor;
        int mTextColor;
        int mTextSize;
        int mWidth;
        int mHeight;
        int mDegrees;
        float mAlpha;

        WatermarkParams(Context context, String mDrawText) {
            this.context = context;
            this.mDrawText = mDrawText;
            mTextColor = Color.parseColor("#ebebeb");
            mBackgroundColor = Color.WHITE;
            mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics());
            mWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, context.getResources().getDisplayMetrics());
            mHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, context.getResources().getDisplayMetrics());
            mDegrees = -30;
            mAlpha = 0.8f;
        }

    }

}

(相关代码在我的github上)

以上,禁止商业用途转载,其他转载请告知,谢谢!

最近的文章

React Native原生模块与React模块之间的数据传递

我眼中的React Native 关于React Native的介绍与评价网上随处可见,比如知乎,初探RN,在我看来,RN是一种新的开发模式,前端语言写界面,写一次多端可用(Android、Ios、微…

前端继续阅读
更早的文章

Spring Boot(三)接入sentry

Sentry是一个实时事件日志记录平台,可以帮助我们记录事件,记录exception,可以看做后端的umeng统计。这里是Java版的文档。网上能找到的sentry教程基本上都是基于raven-jav…

后端继续阅读
comments powered by Disqus