一、项目需求:
因为产品对象用于中老年人,所以产品设计添加了APP全局字体调整大小功能。
这里仿做QQ设置字体大小的功能。
QQ实现的效果是,滚动下面的seekbar,当只有seekbar到达某一个刻度的时候,这时候上部分的效果展示部分会改变文字大小,
但是在拖动过程中字体不会改变。关闭此界面,就可以看到改变文字后app整体的实际文字大小效果了。
-----------------------------------------------------------------------------------------------------------------------------
二、理清一下实现思路:
1、先将一个APP内所有的文本设置级别,大概3--5个级别(具体看项目需求),比如标题栏的TextView我们给他设置级别1(默认24sp) ,类似设置 级别2(默认22sp)等等。
这样做的目的可以方便的我们设置,如果每个Textview大小都乱乱的,那这个文字大小改变的功能也没什么意义了。
2、创建一个类Constant,类中创建一个静态变量,这个变量用于记录当我们拖动seekbar的时候 对应改变。取值范围就是我们seekbar的界点。
Demo我们限制文字大小有四个界点:小、标准、大、特大。
那么静态变量 TEXT_SIZE 取值就有0,1,2,3
public static int TEXT_SIZE = 0;
3、滑动seekbar,当达到界点的时候,改变静态变量TEXT_SIZE的值,并且刷新列表适配器(这个列表是展示文字大小效果的,所以数据是我们自己写死的,
要求达到某个界点才会刷新适配器,绝不可能seekbar有滑动操作我们就执行刷新适配器的)
4、在退出设置字体界面的时候,用sharedPreferences保存,每次进入app的时候读取。
这样在每个Activity或者Fragment 创建View的过程中在 TextView创建的时候给控件动态设置文字的大小
textview.setTextSize(级别默认文字大小+seekbar级别*3);
思路就是这么简单,看懂的可以自己去实现了,有点懵的看下面的例子来深入了解下。
整体思路就是: 一个标记变量,记录要显示文字大小的级别,sharedpreference保存。然后在每个要打开的新的界面创建View的过程中 给TextView动态设置文字大小
注意:不是我修改文字大小之后,整个APP所有界面的TextView都立马改变。
-----------------------------------------------------------------------------------------------------------------------------
三、代码实现
1、首先就是这个SeekBar控件,上面需要有刻度,需要有文字,显然我们用android提供的自带的SeekBar控件已经不满足我们的需求了。
但是,这里我找到了一个很好的自定义控件可以完美的实现这个问题:
资料来源: Android 自定义带刻度的seekbar
这里我加了一些注释
1 public class CustomSeekbar extends View {
2 private final String TAG = "CustomSeekbar";
3 private int width;
4 private int height;
5 private int downX = 0;
6 private int downY = 0;
7 private int upX = 0;
8 private int upY = 0;
9 private int moveX = 0;
10 private int moveY = 0;
11 private float scale = 0;
12 private int perWidth = 0;
13 private Paint mPaint;
14 private Paint mTextPaint;
15 private Paint buttonPaint;
16 private Canvas canvas;
17 private Bitmap bitmap;
18 private Bitmap thumb;
19 private Bitmap spot;
20 private Bitmap spot_on;
21 private int hotarea = 100;//点击的热区
22 private int cur_sections = 2;
23 private ResponseOnTouch responseOnTouch;
24 private int bitMapHeight = 38;//第一个点的起始位置起始,图片的长宽是76,所以取一半的距离
25 private int textMove = 60;//字与下方点的距离,因为字体字体是40px,再加上10的间隔
26 private int[] colors = new int[]{0xffdf5600,0x33000000};//进度条的橙色,进度条的灰色,字体的灰色
27 private int textSize;
28 private int circleRadius;
29 private ArrayList<String> section_title;
30 public CustomSeekbar(Context context) {
31 super(context);
32 }
33 public CustomSeekbar(Context context, AttributeSet attrs) {
34 this(context, attrs, 0);
35 }
36 public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
37 super(context, attrs, defStyleAttr);
38 cur_sections = 0;
39 bitmap = Bitmap.createBitmap(900, 1100, Bitmap.Config.ARGB_8888);
40 canvas = new Canvas();
41 canvas.setBitmap(bitmap);
42 thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large); //这个是滑动图标
43 spot = BitmapFactory.decodeResource(getResources(),R.mipmap.img_setting_seekbar_thumbe); //这个是未滑动到的界点的图标
44 spot_on = BitmapFactory.decodeResource(getResources(),R.mipmap.img_setting_seekbar_thumbe); //这个是已经滑动过的界点的图标
45 bitMapHeight = thumb.getHeight()/2; //这里影响点中的图标的位置 这个正好 不用改
46 textMove = bitMapHeight+ 5; //xqx 这里参数大小要改,不是固定的,具体看项目效果
47 textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()); //文字大小,第二个参数个人设置
48 circleRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());
49 mPaint = new Paint(Paint.DITHER_FLAG);
50 mPaint.setAntiAlias(true);//锯齿不显示
51 mPaint.setStrokeWidth(3);
52 mTextPaint = new Paint(Paint.DITHER_FLAG);
53 mTextPaint.setAntiAlias(true);
54 mTextPaint.setTextSize(textSize);
55 mTextPaint.setColor(0xffb5b5b4);
56 buttonPaint = new Paint(Paint.DITHER_FLAG);
57 buttonPaint.setAntiAlias(true);
58
59 }
60 /**
61 * 实例化后调用,设置bar的段数和文字
62 */
63 public void initData(ArrayList<String> section){
64 if(section != null){
65 section_title = section;
66 }else {
67 //如果没有传入正确的分类级别数据,则默认使用“低”“中”“高”
68 String[] str = new String[]{"低", "中", "高"};
69 section_title = new ArrayList<String>();
70 for (int i = 0; i < str.length; i++) {
71 section_title.add(str[i]);
72 }
73 }
74 }
75
76 @Override
77 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
78 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
79
80 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
81 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
82 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
83 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
84
85 width = widthSize;
86 float scaleX = widthSize / 1080;
87 float scaleY = heightSize / 1920;
88 scale = Math.max(scaleX,scaleY);
89 //控件的高度
90 //height = 185;
91 height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics());
92 setMeasuredDimension(width, height);
93 width = width-bitMapHeight/2;
94 perWidth = (width - section_title.size()*spot.getWidth() - thumb.getWidth()/2) / (section_title.size()-1);
95 hotarea = perWidth/2;
96 }
97
98 @Override
99 protected void onDraw(Canvas canvas) {
100 super.onDraw(canvas);
101 mPaint.setColor(Color.WHITE);
102 mPaint.setStyle(Paint.Style.FILL);
103 mPaint.setAlpha(0);
104 canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
105 canvas.drawBitmap(bitmap, 0, 0, null);
106 mPaint.setAlpha(255);
107 mPaint.setColor(colors[1]);
108 canvas.drawLine(bitMapHeight, height * 2 / 3, width - bitMapHeight - spot_on.getWidth() / 2, height * 2 / 3, mPaint);
109 int section = 0;
110 while(section < section_title.size()){
111 if(section < cur_sections) {
112 mPaint.setColor(colors[0]);
113 canvas.drawLine(thumb.getWidth()/2 + section * perWidth + (section+1) * spot_on.getWidth(),height * 2 / 3,
114 thumb.getWidth()/2 + section * perWidth + (section+1) * spot_on.getWidth() + perWidth,height * 2 / 3,mPaint);
115 canvas.drawBitmap(spot_on, thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(),height * 2 / 3 - spot_on.getHeight()/2,mPaint);
116 }else{
117 mPaint.setAlpha(255);
118 if(section == section_title.size()-1){
119 canvas.drawBitmap(spot, width - spot_on.getWidth() - bitMapHeight/2, height * 2 / 3 - spot.getHeight() / 2, mPaint);
120 }else {
121 canvas.drawBitmap(spot, thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(), height * 2 / 3 - spot.getHeight() / 2, mPaint);
122 }
123 }
124
125 if(section == section_title.size()-1) {
126 canvas.drawText(section_title.get(section), width - spot_on.getWidth()- bitMapHeight/4 - textSize / 2, height * 2 / 3 - textMove, mTextPaint);
127 }else{
128 canvas.drawText(section_title.get(section), thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(), height * 2 / 3 - textMove, mTextPaint);
129 }
130 section++;
131 }
132 if(cur_sections == section_title.size()-1){
133 canvas.drawBitmap(thumb, width - spot_on.getWidth() - bitMapHeight/2 - thumb.getWidth() / 2,
134 height * 2 / 3 - bitMapHeight, buttonPaint);
135 }else {
136 canvas.drawBitmap(thumb, thumb.getWidth()/2 + cur_sections * perWidth + cur_sections * spot_on.getWidth() - thumb.getWidth()/4 ,
137 height * 2 / 3 - bitMapHeight, buttonPaint);
138 }
139 }
140
141 @Override
142 public boolean onTouchEvent(MotionEvent event) {
143 super.onTouchEvent(event);
144 switch (event.getAction()) {
145 case MotionEvent.ACTION_DOWN:
146 thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large);
147 downX = (int) event.getX();
148 downY = (int) event.getY();
149 responseTouch(downX, downY);
150 break;
151 case MotionEvent.ACTION_MOVE:
152 thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large);
153 moveX = (int) event.getX();
154 moveY = (int) event.getY();
155 responseTouch(moveX, moveY);
156 break;
157 case MotionEvent.ACTION_UP:
158 thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large);
159 upX = (int) event.getX();
160 upY = (int) event.getY();
161 responseTouch(upX, upY);
162 responseOnTouch.onTouchResponse(cur_sections);
163 break;
164 }
165 return true;
166 }
167 private void responseTouch(int x, int y){
168 if(x <= width-bitMapHeight/2) {
169 cur_sections = (x + perWidth / 3) / perWidth;
170 }else{
171 cur_sections = section_title.size()-1;
172 }
173 invalidate();
174 }
175
176 //设置监听
177 public void setResponseOnTouch(ResponseOnTouch response){
178 //注意 ,这里是接口,实现你到达界点的监听事件,因为这个自定义控件继承的View而不是SeekBar,所以只能使用接口实现监听
179 responseOnTouch = response;
180 }
181
182
183 //设置进度
184 public void setProgress(int progress){
185 cur_sections = progress;
186 invalidate();
187 }
188 }
2、根据这个自定义CustomSeekbar控件,我们首先要建一个接口
public interface ResponseOnTouch {
public void onTouchResponse(int volume);
}
3、创建一个类。设置一个静态属性
public class Constant {
public static int TEXT_SIZE = 0;
}
4、接下来写字体设置后的效果界面:qq的效果界面有两个,一个是聊天的界面,一个是列表的界面。
这里我们只展示列表的界面
列表代码就不展示了
直接看如何使用CustomSeekbar
1 private CustomSeekbar seekBar;
2 seekBar = (CustomSeekbar) findViewById(R.id.progressBar);
3 //这个集合用于给自定义SeekBar设置界点级别,集合里有几个数据,就有几个界点
4 ArrayList<String> volume_sections = new ArrayList<String>();
5 volume_sections.add("小");
6 volume_sections.add("标准");
7 volume_sections.add("大");
8 volume_sections.add("特大");
9 seekBar.initData(volume_sections);
10 seekBar.setProgress(0); //设置默认级别
11
12
13 seekBar.setResponseOnTouch(this);//activity实现了下面的接口ResponseOnTouch,每次touch会回调onTouchResponse
实现接口:
@Override
public void onTouchResponse(int volume) {
Toast.makeText(this,"volume-->"+volume,Toast.LENGTH_SHORT).show();
//参数volume就是级别,如果我们集合有4个数据 那么volume的取值就为0、1、2、3
Constant.TEXT_SIZE = volume;
//这里写sharedpreferences保存该静态变量
//刷新列表 ,查看文字改变后的效果
adapter.notifyDataSetChanged();
}
列表适配器中对textview设置大小的代码:
holder.community_doctor_name.setTextSize(该TextView控件级别默认文字大小+ Constant.TEXT_SIZE*5);
效果图:
后续补上。
个人思路,实现的局限性是有的,大家有修改意见欢迎提出。