Android项目实战(二十三)(仿QQ设置App全局字体大小)

著论准过秦,作赋拟子虚。这篇文章主要讲述Android项目实战(二十三):仿QQ设置App全局字体大小相关的知识,希望能为你提供帮助。
原文:Android项目实战(二十三):仿QQ设置App全局字体大小一、项目需求:
因为产品对象用于中老年人,所以产品设计添加了APP全局字体调整大小功能。
这里仿做QQ设置字体大小的功能。

Android项目实战(二十三)(仿QQ设置App全局字体大小)

文章图片
   
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;

【Android项目实战(二十三)(仿QQ设置App全局字体大小)】 
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   
这里我加了一些注释
Android项目实战(二十三)(仿QQ设置App全局字体大小)

文章图片
Android项目实战(二十三)(仿QQ设置App全局字体大小)

文章图片
1 public class CustomSeekbar extends View { 2private final String TAG = "CustomSeekbar"; 3private int width; 4private int height; 5private int downX = 0; 6private int downY = 0; 7private int upX = 0; 8private int upY = 0; 9private int moveX = 0; 10private int moveY = 0; 11private float scale = 0; 12private int perWidth = 0; 13private Paint mPaint; 14private Paint mTextPaint; 15private Paint buttonPaint; 16private Canvas canvas; 17private Bitmap bitmap; 18private Bitmap thumb; 19private Bitmap spot; 20private Bitmap spot_on; 21private int hotarea = 100; //点击的热区 22private int cur_sections = 2; 23private ResponseOnTouch responseOnTouch; 24private int bitMapHeight = 38; //第一个点的起始位置起始,图片的长宽是76,所以取一半的距离 25private int textMove = 60; //字与下方点的距离,因为字体字体是40px,再加上10的间隔 26private int[] colors = new int[]{0xffdf5600,0x33000000}; //进度条的橙色,进度条的灰色,字体的灰色 27private int textSize; 28private int circleRadius; 29private ArrayList< String> section_title; 30public CustomSeekbar(Context context) { 31super(context); 32} 33public CustomSeekbar(Context context, AttributeSet attrs) { 34this(context, attrs, 0); 35} 36public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr) { 37super(context, attrs, defStyleAttr); 38cur_sections = 0; 39bitmap = Bitmap.createBitmap(900, 1100, Bitmap.Config.ARGB_8888); 40canvas = new Canvas(); 41canvas.setBitmap(bitmap); 42thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large); //这个是滑动图标 43spot = BitmapFactory.decodeResource(getResources(),R.mipmap.img_setting_seekbar_thumbe); //这个是未滑动到的界点的图标 44spot_on = BitmapFactory.decodeResource(getResources(),R.mipmap.img_setting_seekbar_thumbe); //这个是已经滑动过的界点的图标 45bitMapHeight = thumb.getHeight()/2; //这里影响点中的图标的位置这个正好 不用改 46textMove = bitMapHeight+ 5; //xqx这里参数大小要改,不是固定的,具体看项目效果 47textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()); //文字大小,第二个参数个人设置 48circleRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()); 49mPaint = new Paint(Paint.DITHER_FLAG); 50mPaint.setAntiAlias(true); //锯齿不显示 51mPaint.setStrokeWidth(3); 52mTextPaint = new Paint(Paint.DITHER_FLAG); 53mTextPaint.setAntiAlias(true); 54mTextPaint.setTextSize(textSize); 55mTextPaint.setColor(0xffb5b5b4); 56buttonPaint = new Paint(Paint.DITHER_FLAG); 57buttonPaint.setAntiAlias(true); 58 59} 60/** 61* 实例化后调用,设置bar的段数和文字 62*/ 63public void initData(ArrayList< String> section){ 64if(section != null){ 65section_title = section; 66}else { 67//如果没有传入正确的分类级别数据,则默认使用“ 低” “ 中” “ 高” 68String[] str = new String[]{"低", "中", "高"}; 69section_title = new ArrayList< String> (); 70for (int i = 0; i < str.length; i++) { 71section_title.add(str[i]); 72} 73} 74} 75 76@Override 77protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 78super.onMeasure(widthMeasureSpec, heightMeasureSpec); 79 80int widthMode = MeasureSpec.getMode(widthMeasureSpec); 81int widthSize = MeasureSpec.getSize(widthMeasureSpec); 82int heightMode = MeasureSpec.getMode(heightMeasureSpec); 83int heightSize = MeasureSpec.getSize(heightMeasureSpec); 84 85width = widthSize; 86float scaleX = widthSize / 1080; 87float scaleY = heightSize / 1920; 88scale = Math.max(scaleX,scaleY); 89//控件的高度 90//height = 185; 91height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()); 92setMeasuredDimension(width, height); 93width = width-bitMapHeight/2; 94perWidth = (width - section_title.size()*spot.getWidth() - thumb.getWidth()/2) / (section_title.size()-1); 95hotarea = perWidth/2; 96} 97 98@Override 99protected void onDraw(Canvas canvas) { 100super.onDraw(canvas); 101mPaint.setColor(Color.WHITE); 102mPaint.setStyle(Paint.Style.FILL); 103mPaint.setAlpha(0); 104canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); 105canvas.drawBitmap(bitmap, 0, 0, null); 106mPaint.setAlpha(255); 107mPaint.setColor(colors[1]); 108canvas.drawLine(bitMapHeight, height * 2 / 3, width - bitMapHeight - spot_on.getWidth() / 2, height * 2 / 3, mPaint); 109int section = 0; 110while(section < section_title.size()){ 111if(section < cur_sections) { 112mPaint.setColor(colors[0]); 113canvas.drawLine(thumb.getWidth()/2 + section * perWidth + (section+1) * spot_on.getWidth(),height * 2 / 3, 114thumb.getWidth()/2 + section * perWidth + (section+1) * spot_on.getWidth() + perWidth,height * 2 / 3,mPaint); 115canvas.drawBitmap(spot_on, thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(),height * 2 / 3 - spot_on.getHeight()/2,mPaint); 116}else{ 117mPaint.setAlpha(255); 118if(section == section_title.size()-1){ 119canvas.drawBitmap(spot,width - spot_on.getWidth() - bitMapHeight/2, height * 2 / 3 - spot.getHeight() / 2, mPaint); 120}else { 121canvas.drawBitmap(spot, thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(), height * 2 / 3 - spot.getHeight() / 2, mPaint); 122} 123} 124 125if(section == section_title.size()-1) { 126canvas.drawText(section_title.get(section), width - spot_on.getWidth()- bitMapHeight/4 - textSize / 2, height * 2 / 3 - textMove, mTextPaint); 127}else{ 128canvas.drawText(section_title.get(section), thumb.getWidth()/2 + section * perWidth + section * spot_on.getWidth(), height * 2 / 3 - textMove, mTextPaint); 129} 130section++; 131} 132if(cur_sections == section_title.size()-1){ 133canvas.drawBitmap(thumb, width - spot_on.getWidth() - bitMapHeight/2 - thumb.getWidth() / 2, 134height * 2 / 3 - bitMapHeight, buttonPaint); 135}else { 136canvas.drawBitmap(thumb, thumb.getWidth()/2 + cur_sections * perWidth + cur_sections * spot_on.getWidth() - thumb.getWidth()/4 , 137height * 2 / 3 - bitMapHeight, buttonPaint); 138} 139} 140 141@Override 142public boolean onTouchEvent(MotionEvent event) { 143super.onTouchEvent(event); 144switch (event.getAction()) { 145case MotionEvent.ACTION_DOWN: 146thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large); 147downX = (int) event.getX(); 148downY = (int) event.getY(); 149responseTouch(downX, downY); 150break; 151case MotionEvent.ACTION_MOVE: 152thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large); 153moveX = (int) event.getX(); 154moveY = (int) event.getY(); 155responseTouch(moveX, moveY); 156break; 157case MotionEvent.ACTION_UP: 158thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.img_setting_seekbar_thumbe_large); 159upX = (int) event.getX(); 160upY = (int) event.getY(); 161responseTouch(upX, upY); 162responseOnTouch.onTouchResponse(cur_sections); 163break; 164} 165return true; 166} 167private void responseTouch(int x, int y){ 168if(x < = width-bitMapHeight/2) { 169cur_sections = (x + perWidth / 3) / perWidth; 170}else{ 171cur_sections = section_title.size()-1; 172} 173invalidate(); 174} 175 176//设置监听 177public void setResponseOnTouch(ResponseOnTouch response){ 178//注意 ,这里是接口,实现你到达界点的监听事件,因为这个自定义控件继承的View而不是SeekBar,所以只能使用接口实现监听 179responseOnTouch = response; 180} 181 182 183//设置进度 184public void setProgress(int progress){ 185cur_sections = progress; 186invalidate(); 187} 188 }

CustomSeekbar.class 
2、根据这个自定义CustomSeekbar控件,我们首先要建一个接口
public interface ResponseOnTouch { public void onTouchResponse(int volume); }

 
3、创建一个类。设置一个静态属性 
public class Constant {
public static int TEXT_SIZE = 0;
}

 
4、接下来写字体设置后的效果界面:qq的效果界面有两个,一个是聊天的界面,一个是列表的界面。
这里我们只展示列表的界面
列表代码就不展示了
直接看如何使用CustomSeekbar
1private CustomSeekbar seekBar; 2seekBar = (CustomSeekbar) findViewById(R.id.progressBar); 3//这个集合用于给自定义SeekBar设置界点级别,集合里有几个数据,就有几个界点 4ArrayList< String> volume_sections = new ArrayList< String> (); 5volume_sections.add("小"); 6volume_sections.add("标准"); 7volume_sections.add("大"); 8volume_sections.add("特大"); 9seekBar.initData(volume_sections); 10seekBar.setProgress(0); //设置默认级别 11 12 13seekBar.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);

 
效果图:
后续补上。
 
个人思路,实现的局限性是有的,大家有修改意见欢迎提出。







    推荐阅读