IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 自定义View:按字母索引排序 -> 正文阅读

[移动开发]自定义View:按字母索引排序

Android开发常见的需求: 按拼音排序

Android常见需求,按照汉字首字母排序,从A~Z,右侧自定义一个字母列表,手指可以滑动选择,并和RecyclerView联动的效果。
如图:
在这里插入图片描述
左侧RecyclerView建议使用多布局类型,以后好做头部悬停效果。右侧要自定义View来实现,触摸滑动刷新界面了。

首先,计算View的宽高和字母所占的空间高度;

1、计算View的宽度

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //取出宽高的MeasureSpec  Mode 和Size
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidth = 0, measureHeight = 0;//最终测量出来的宽高
        //得到合适宽度:
        Rect indexBounds = new Rect();//存放每个绘制的index的Rect区域
        String index;//每个要绘制的index内容
        for (int i = 0; i < firstLetters.size(); i++) {
            index = firstLetters.get(i);
            mPaint.getTextBounds(index, 0, index.length(), indexBounds);//测量计算文字所在矩形,可以得到宽高
            measureWidth = Math.max(indexBounds.width(), measureWidth);//循环结束后,得到index的最大宽度
        }
  
        //得到合适的宽度:
        switch (wMode) {
            case MeasureSpec.EXACTLY:
                measureWidth = wSize;
                break;
            case MeasureSpec.AT_MOST:
            //wrap_content
                measureWidth = Math.min(measureWidth, wSize);//wSize此时是父控件能给子View分配的最大空间
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }
    }

2、先算出整个View的高度,再除以字母个数,就是每个字母所用的平均空间高度,

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //取出宽高的MeasureSpec  Mode 和Size
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidth = 0, measureHeight = 0;//最终测量出来的宽高
        //得到合适宽度:
        Rect indexBounds = new Rect();//存放每个绘制的index的Rect区域
        String index;//每个要绘制的index内容
        for (int i = 0; i < firstLetters.size(); i++) {
            index = firstLetters.get(i);
            mPaint.getTextBounds(index, 0, index.length(), indexBounds);//测量计算文字所在矩形,可以得到宽高
            measureWidth = Math.max(indexBounds.width(), measureWidth);//循环结束后,得到index的最大宽度
            measureHeight = Math.max(indexBounds.height(), measureHeight);//循环结束后,得到index的最大高度,然后*size
        }
        measureHeight *= firstLetters.size();
        measureHeight += (getPaddingBottom() + getPaddingTop());
       

        //得到合适的高度:
        switch (hMode) {
            case MeasureSpec.EXACTLY:
                measureHeight = hSize;
                break;
            case MeasureSpec.AT_MOST:
            	//wrap_content
                measureHeight = Math.min(measureHeight, hSize);//wSize此时是父控件能给子View分配的最大空间
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }
    }

高度算出来后,可以在 onSizeChanged() 方法里面获取;

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        if (mIsHalfWidth){
            mCircleRadius = mWidth / 2F;
        }

        if (null == firstLetters || firstLetters.isEmpty()) {
            return;
        }
        //计算每个字母平均占据空间高度
        computeGapHeight();
    }

获取高度后,除去字母个数可得每个字母所占平均高度:

 private void computeGapHeight() {
 		//高度减去padding,除以字母个数
        mCellHeight = (mHeight - getPaddingTop() - getPaddingBottom()) / firstLetters.size();
    }

绘制字母和小圆圈

onDraw() 方法里吗进行绘制。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int paddingTop = getPaddingTop();
        String letter;
        for (int i = 0; i < firstLetters.size(); i++) {
        	//选中的字母
            if (choosePos == i) {
            //如果有字体大小变化需求的话
//                mPaint.setTextSize();
                mPaint.setColor(mSelectTextColor);
                //计算圆圈圆心坐标,x,y,和半径
                //x坐标位于View中间,宽度的一半
                //y坐标是每个字母的中心位置,字母所占高度的一半,再加上边几个字母的距离,paddingTop+mCellHeight * i
                canvas.drawCircle(mWidth / 2F, paddingTop + mCellHeight / 2F + mCellHeight * i, mCircleRadius, mCirclePaint);
            } else {
                mPaint.setColor(mUnSelectTextColor);
            }

            letter = firstLetters.get(i);
            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
            //计算字母基线坐标,mCellHeight 字母平均所占空间高度,减去字母绘制的高度除以2就是字体基线位置,还要加上距离顶部的距离paddingTop,和mCellHeight * i
            // 测量文字宽高还可以用Rect  mTextRect = new Rect();
            //mPaint.getTextBounds(letter, 0, letter.length(), mTextRect);
            //  int textWidth = mTextRect.width();
           // int textHeight = mTextRect.height();
          //  float baseLine = paddingTop + (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);
            float baseLine =(mCellHeight - fontMetrics.bottom - fontMetrics.top) / 2F;

            canvas.drawText(letter, mWidth / 2F - mPaint.measureText(letter) / 2F,
                    paddingTop + baseLine + mCellHeight * i, mPaint);

        }


    }

然后写OnTouchEvent 事件

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                float y = event.getY();
				//计算触摸位置,获取y坐标,相对于当前View的Y坐标
                int indexSelected = (int) ((y - getPaddingTop()) / mCellHeight);

                if (indexSelected < 0) {
                    indexSelected = 0;
                } else if (indexSelected >= firstLetters.size()) {
                    indexSelected = firstLetters.size() - 1;
                }

                if (choosePos != indexSelected) {
                    choosePos = indexSelected;
                    invalidate();
                    if (mOnSelectItemListener != null) {
                        mOnSelectItemListener.onItemSelect(firstLetters.get(choosePos));
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mOnReleaseListener != null) {
                    mOnReleaseListener.onRelease();
                }
                break;
        }
        return true;
    }

最后就是和RecyclerView联动了.

  • MySlideBarView
    整体代码:


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;



import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MySlideBarView extends View {

    /**
     * 字母表
     */
    private String[] mLetters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
    /**
     * 默认选中时,文字颜色
     */
    private final int mDefaultSelectTextColor = Color.parseColor("#FFFFFF");
    /**
     * 默认未选中时,文字颜色
     */
    private final int mDefaultUnSelectTextColor = Color.parseColor("#202020");
    /**
     * 默认选中时,滑动条背景颜色
     */
    private final int mDefaultSelectBgColor = Color.RED;


    /**
     * 字母大小
     */
    private final float mDefaultLetterSize = 14;
    //View的宽高
    private int mWidth, mHeight;

    /**
     * 每个字母的高度
     */
    private int mCellHeight;
    /**
     * 选择回调
     */
    private OnSelectItemListener mOnSelectItemListener;
    /**
     * 释放手指回调
     */
    private OnReleaseListener mOnReleaseListener;
    /**
     * 画笔
     */
    private Paint mPaint, mCirclePaint;
    /**
     * 文字区域,保存为成员变量是为了复用
     */
    private Rect mTextRect;

    private int mSelectTextColor;
    private int mUnSelectTextColor;
    /**
     * 选中字的圆形的的背景色
     */
    private int mSelectTextBgColor;

    /**
     * 圆形的半径
     */
    private float mCircleRadius;

    /**
     * 小圆的半径是否是宽度的一半
     */
    private boolean mIsHalfWidth;

    /**
     * 控件的背景圆角半径
     */
    private float mCorner;

    /**
     * 控件的背景颜色
     */
    private int mBgColor;

    /**
     * 字母的字体大小
     */
    private float mLetterSize;

    /**
     * 首字母集合
     */
    private List<String> firstLetters ;

    private int choosePos;

    public MySlideBarView(Context context) {
        this(context, null);
    }

    public MySlideBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MySlideBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }


    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySlideBarView, defStyleAttr, 0);

        mSelectTextColor = typedArray.getColor(R.styleable.MySlideBarView_slb_select_txt_color, mDefaultSelectTextColor);
        mUnSelectTextColor = typedArray.getColor(R.styleable.MySlideBarView_slb_un_select_txt_color, mDefaultUnSelectTextColor);
        mLetterSize = typedArray.getDimensionPixelSize(R.styleable.MySlideBarView_slb_letter_size,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDefaultLetterSize, getResources().getDisplayMetrics()));
        mSelectTextBgColor = typedArray.getColor(R.styleable.MySlideBarView_slb_select_text_bg_color, mDefaultSelectBgColor);

        mBgColor = typedArray.getColor(R.styleable.MySlideBarView_slb_bg_color, Color.GRAY);
        mCorner = typedArray.getDimension(R.styleable.MySlideBarView_slb_corner, 15f);
        mCircleRadius = typedArray.getDimension(R.styleable.MySlideBarView_slb_circle_radius,0f);
        mIsHalfWidth = typedArray.getBoolean(R.styleable.MySlideBarView_slb_is_circle_radius_equals_width,true);
        typedArray.recycle();

        firstLetters = Arrays.asList(mLetters);

        mTextRect = new Rect();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mPaint.setColor(mUnSelectTextColor);
        mPaint.setTextSize(mLetterSize);

        mCirclePaint.setColor(mSelectTextBgColor);

        //设置默认背景
        float[] outerRadii = {mCorner, mCorner, mCorner, mCorner, mCorner, mCorner, mCorner, mCorner};//外矩形        左上、右上、右下、左下的圆角半径

        RoundRectShape roundRectShape = new RoundRectShape(outerRadii, null, null);

        ShapeDrawable drawable = new ShapeDrawable(roundRectShape);
        drawable.getPaint().setColor(mBgColor);
        drawable.getPaint().setAntiAlias(true);
        drawable.getPaint().setStyle(Paint.Style.FILL);

        if (getBackground() == null) {
            setBackground(drawable);
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //取出宽高的MeasureSpec  Mode 和Size
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidth = 0, measureHeight = 0;//最终测量出来的宽高
        //得到合适宽度:
        Rect indexBounds = new Rect();//存放每个绘制的index的Rect区域
        String index;//每个要绘制的index内容
        for (int i = 0; i < firstLetters.size(); i++) {
            index = firstLetters.get(i);
            mPaint.getTextBounds(index, 0, index.length(), indexBounds);//测量计算文字所在矩形,可以得到宽高
            measureWidth = Math.max(indexBounds.width(), measureWidth);//循环结束后,得到index的最大宽度
            measureHeight = Math.max(indexBounds.height(), measureHeight);//循环结束后,得到index的最大高度,然后*size
        }
        measureHeight *= firstLetters.size();
        measureHeight += (getPaddingBottom() + getPaddingTop());
        //得到合适的宽度:
        switch (wMode) {
            case MeasureSpec.EXACTLY:
                measureWidth = wSize;
                break;
            case MeasureSpec.AT_MOST:
                measureWidth = Math.min(measureWidth, wSize);//wSize此时是父控件能给子View分配的最大空间
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }

        //得到合适的高度:
        switch (hMode) {
            case MeasureSpec.EXACTLY:
                measureHeight = hSize;
                break;
            case MeasureSpec.AT_MOST:
                measureHeight = Math.min(measureHeight, hSize);//wSize此时是父控件能给子View分配的最大空间
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }

        setMeasuredDimension(measureWidth, measureHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int paddingTop = getPaddingTop();
        String letter;
        for (int i = 0; i < firstLetters.size(); i++) {
            letter = firstLetters.get(i);
            //测量文字宽高
            mPaint.getTextBounds(letter, 0, letter.length(), mTextRect);
            int textWidth = mTextRect.width();
            int textHeight = mTextRect.height();
            float baseLine = paddingTop + (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);

            if (choosePos == i) {
//                mPaint.setTextSize();
                mPaint.setColor(mSelectTextColor);
                canvas.drawCircle(mWidth / 2F, paddingTop + mCellHeight / 2F + mCellHeight * i, mCircleRadius, mCirclePaint);
            } else {
                mPaint.setColor(mUnSelectTextColor);
            }


            canvas.drawText(letter, mWidth / 2F - textWidth / 2F,
                    baseLine , mPaint);

        }


    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        if (mIsHalfWidth){
            mCircleRadius = mWidth / 2F;
        }

        if (null == firstLetters || firstLetters.isEmpty()) {
            return;
        }
        computeGapHeight();
    }

    /**
     * 以下情况调用:
     * 1 在数据源改变
     * 2 控件size改变时
     * 计算gapHeight
     */
    private void computeGapHeight() {
        mCellHeight = (mHeight - getPaddingTop() - getPaddingBottom()) / firstLetters.size();
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                float y = event.getY();

                int indexSelected = (int) ((y - getPaddingTop()) / mCellHeight);

                if (indexSelected < 0) {
                    indexSelected = 0;
                } else if (indexSelected >= firstLetters.size()) {
                    indexSelected = firstLetters.size() - 1;
                }

                if (choosePos != indexSelected) {
                    choosePos = indexSelected;
                    invalidate();
                    if (mOnSelectItemListener != null) {
                        mOnSelectItemListener.onItemSelect(firstLetters.get(choosePos));
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mOnReleaseListener != null) {
                    mOnReleaseListener.onRelease();
                }
                break;
        }
        return true;
    }

    /**
     * 外部传入数据
     *
     * @param data
     */
    public void setData(List<String> data) {
        firstLetters = new ArrayList<>();
        firstLetters.addAll(data);
        requestLayout();
        computeGapHeight();
    }

    /**
     * 点击字母回调接口
     */
    public interface OnSelectItemListener {
        /**
         * 选择时回调
         *
         * @param selectLetter 选中的字母
         */
        void onItemSelect(String selectLetter);


    }

    public interface OnReleaseListener{
        /**
         * 松手取消选中时回调
         */
        void onRelease();
    }

    public void setOnSelectItemListener(OnSelectItemListener mOnSelectItemListener) {
        this.mOnSelectItemListener = mOnSelectItemListener;
    }

    public void setOnReleaseListener(OnReleaseListener mOnReleaseListener) {
        this.mOnReleaseListener = mOnReleaseListener;
    }

    /**
     * 根据字母刷新View
     *
     * @param word 选中的元素
     */
    public void invalidateByWord(String word) {
        for (int i = 0; i < firstLetters.size(); i++) {
            if (firstLetters.get(i).equals(word) && choosePos != i) {
                choosePos = i;
                invalidate();
            }
        }
    }


    public void setSelectTextColor(int mSelectTextColor) {
        this.mSelectTextColor = mSelectTextColor;
    }

    public void setUnSelectTextColor(int mUnSelectTextColor) {
        this.mUnSelectTextColor = mUnSelectTextColor;
    }

    public void setSelectBgColor(int mSelectBgColor) {
        this.mSelectTextBgColor = mSelectBgColor;
    }
}


自定义属性:

 <declare-styleable name="MySlideBarView">
        <!-- 选中时,文字颜色 -->
        <attr name="slb_select_txt_color" format="color|reference" />
        <!-- 非选中时,文字颜色 -->
        <attr name="slb_un_select_txt_color" format="color|reference" />
        <!-- 选中时,滑动条背景颜色 -->
        <attr name="slb_select_text_bg_color" format="color|reference" />
        <!--  字母大小      -->
        <attr name="slb_letter_size" format="dimension" />
        <!--  View的背景色      -->
        <attr name="slb_bg_color" format="color|reference" />
        <!--  背景圆角半径 -->
        <attr name="slb_corner" format="dimension" />
        <!-- 小圆的半径  -->
        <attr name="slb_circle_radius" format="dimension" />
        <!-- 小圆的半径等于宽度一半  默认true-->
        <attr name="slb_is_circle_radius_equals_width" format="boolean" />
    </declare-styleable>

RecyclerView设置滚动监听事件

 recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int scrollState) {
                super.onScrollStateChanged(recyclerView, scrollState);
                mScrollState = scrollState;
                if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
                    isClick = false;
                }
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (mScrollState != -1) {
                    if (!isClick) {
                        //第一个可见的位置
                        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                        //判断是当前layoutManager是否为LinearLayoutManager
                        // 只有LinearLayoutManager才有查找第一个和最后一个可见view位置的方法
                        int firstItemPosition = 0;
                        if (layoutManager instanceof LinearLayoutManager) {
                            LinearLayoutManager linearManager = (LinearLayoutManager) layoutManager;
                            //获取第一个可见view的位置
                            firstItemPosition = linearManager.findFirstVisibleItemPosition();
                        }

                        mySlideBarView.invalidateByWord(sectionAuthors.get(firstItemPosition).header);

                    }

                    if (mScrollState == RecyclerView.SCROLL_STATE_IDLE) {
                        mScrollState = -1;

                    }
                }
            }
        });
   mySlideBarView.setOnSelectItemListener(new MySlideBarView.OnSelectItemListener() {
            @Override
            public void onItemSelect(String selectLetter) {
                //循环判断点击的拼音导航栏和集合中姓名的首字母,如果相同recyclerView就跳转指定位置
                for (int i = 0; i < sectionAuthors.size(); i++) {
                    if (sectionAuthors.get(i).header.equals(selectLetter)) {

                        isClick = true;
                        recycler.smoothScrollToPosition(i);
                        break;
                    }
                }
            }

        });

布局文件

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal"
        android:overScrollMode="never" />

    <com.widget.slidebar.MySlideBarView
        android:id="@+id/slide_bar"
        android:layout_width="20dp"
        android:layout_height="395dp"
        android:layout_gravity="end"
        android:layout_marginTop="56dp"
        app:slb_bg_color="#EAEAEA"
        app:slb_circle_radius="7dp"
        app:slb_corner="9dp"
        app:slb_is_circle_radius_equals_width="false"
        app:slb_letter_size="10sp"
        app:slb_select_text_bg_color="@color/_78303B" />

</FrameLayout>
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:58:42  更:2021-08-27 11:59:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:39:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码