Android項目實戰(二十三):仿QQ設置App全局字體大小

来源:http://www.cnblogs.com/xqxacm/archive/2016/08/17/5779762.html
-Advertisement-
Play Games

一、項目需求: 因為產品對象用於中老年人,所以產品設計添加了APP全局字體調整大小功能。 這裡仿做QQ設置字體大小的功能。 QQ實現的效果是,滾動下麵的seekbar,當只有seekbar到達某一個刻度的時候,這時候上部分的效果展示部分會改變文字大小, 但是在拖動過程中字體不會改變。關閉此界面,就可 ...


一、項目需求:

因為產品對象用於中老年人,所以產品設計添加了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 }
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

 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);

 

效果圖:

後續補上。

 

個人思路,實現的局限性是有的,大家有修改意見歡迎提出。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本人使用Android開發有一段時間了,但是本身沒有系統學,而且多年專註服務端開發,總覺得因為項目需要接觸Android移動端開發只是暫時的,所以沒有太上心,結果碰到一個大難題折騰了一天,最後被有經驗的小伙伴提示了一下才迎刃而解,感覺無地自容的同時,又非常竊喜,畢竟跨過一個一個的坎,就成長了,在這裡 ...
  • 一、創建一個Session,並且對其進行初始化 包含三種創建方式: 1、+ (NSURLSession *)sharedSession 單例模式 2、+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)con ...
  • 1)首先得遵守協議UITextFieldDelegate @interface userInfoViewController()<UITextFieldDelegate> 2)設置代理(下麵的self都是輸入框所在的父view) textField.delegate = self; 3)實現UITe ...
  • Xcode 中iOS工程模版: 1.Application類型: Master-detail Application. 可以構建樹形結構導航模式應用,生成的代碼中包含了導航控制器和表示圖控制器。(表示圖控制器指的是導航控制器里的界面); Game. 構建基於iOS的游戲應用; Page-Based ...
  • 第三方控制項類: 1、提示框 MBProgressHUD: 是一款非常強大的、提供多種樣式的提示框。使用起來簡單、方便。可以在GitHub上查看具體的使用方法。 https://github.com/jdg/MBProgressHUD 2、無限迴圈、自動圖片輪播器 SDCycleScrollView ...
  • TextView 文本框 EditText控制項 Button 與 ImageButton ImageView RadioButton CheckBox覆選框 TextView 文本框 ,用於顯示文本的控制項 1) 代碼 <TextView android:layout_width="fill_pare ...
  • android閃頻的實現非常簡單,使用Handler對象的postDelayed()方法就可以實現。在這個方法里傳遞一個Runnable對象和一個延遲的時間。該方法實現了一個延遲執行的效果,延遲的時間由第2個參數指定,單位是毫秒。第一個參數是Runnable對象,裡面包含了延遲後需要執行的操作。我在 ...
  • <!--?xml version="1.0" encoding="UTF-8" standalone="no"?--> 目前隨著公司開發模式的變更,swift也顯得越發重要,相對來說,swift語言更加簡潔,嚴謹.但對於我來說,感覺swift細節的處理很繁瑣,可能是還沒適應的緣故吧.基本每寫一句代碼 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...