• Android--底部导航栏的动态替换方案


    1、通常来说,一般情况下,我们的app的BottomTab会有集中实现方式。

    1. 自定义view,然后自己写逻辑去实现互斥。
      • 自由度最高,因为啥都是自己写的。
    2. 使用RadioGroup+RadioButton去实现底部的Tab。
      • 自由度比极高,如果想实现搞复杂度的话可以重写RadioButton。
    3. 使用google design包里面的 TabLayout去实现。
      • 可上、可下、可以滑动
      • 偷懒的话可以根据已有api来设置一些资源,也可以setCustomView()
    4. 使用google design包里面的BottomNavigationView去实现。
      • 使用menu设置资源
      • 有默认的动画效果。

    2、今天来讲一下基于TabLayout来实现的根据后台下发实现动态替换底部导航资源图片的方法。

    • 因为动态替换肯定意味着下载资源,所以先讲一下IntentService

      • IntentService也是一个service,只不过google帮我们在里面封装并维护了一个HandlerThread,里面的操作都是异步的。
      • 当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。
      • 如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,使用串行的方式,执行完自动结束。
    • 这里面最重要的方法 onHandlerIntent(Intent intent)
      •     @Override
            protected void onHandleIntent(Intent intent) {
                if (intent != null) {
                    final String action = intent.getAction();
                    if (ACTION_FOO.equals(action)) {
                        // 在这里面处理耗时任务,当所有的耗时任务都结束以后,IntentService会自动的finish掉,不需要开发者关心。
                    }
                }
            }
    3、选择IntentService的原因(下面的这几个操作都是耗时操作,所以我们干脆都封装到这service里面,我们只需要在合适的时机去启动这个Service就ok了)

      • 需要下载资源压缩包
        • 因为是动态替换,所以必然涉及到预下载,所以数据格式要先定好(下面是数据格式)。
        • {
                  "currentInfo":{//当前样式
                      "id":"111",
                      "imageZipUrl":"http://oc8ql3urp.bkt.clouddn.com/currentInfo.zip",
                      "tabNamesList":[
                              "首页1","产品1","发现1","我的1"
                      ],
                      "tabColorNormal":"B0C4DE",
                      "tabColorHighlight":"F7B62D",
                      "startTime":"1517846400000",
                      "deadLineTime":"1518710400000"
                  },
                  "nextInfo":{//下一次要展示的样式
                      "id":"111",
                      "imageZipUrl":"http://oc8ql3urp.bkt.clouddn.com/nextInfo.zip",
                      "tabNamesList":[
                              "首页2","产品2","发现2","我的2"
                      ],
                      "tabColorNormal":"B0C4DE",
                      "tabColorHighlight":"FE6246",
                      "startTime":"1417846400000",
                      "deadLineTime":"1518710400000"
                  }
              }
      • 需要存放资源压缩包
        • 下载和存放文件的代码(我这里使用的是Retrofit进行下载的)
        • // 下载文件
                      Response<ResponseBody> zipFile = ServiceGenerator.createService(HomeService.class)
                              .downloadFileRetrofit(getFileDownLoadUrl(homeTabImageInfoBean, type))
                              .execute();
          
                      // 得到文件流
                      ResponseBody zipBody = zipFile.body();
          
                      LogUtils.d("HomeTabImageDownLoadInt", "下载完成---");
          
                      // 创建一个文件
                      File zipDirectory = new File(FilePathUtil.getHuaShengHomeTabZipDirectory(getApplicationContext())
                              + createZipFileName(homeTabImageInfoBean, type));
          
                      // 如果文件不存在,则创建文件夹
                      if (!zipDirectory.exists()) {
                          zipDirectory.createNewFile();
                      }
          
                      // 保存文件
                      FileUtils.writeFile2Disk(zipBody, zipDirectory);
      • 需要解压资源压缩方
        • 解压的话就是使用java里面的那几个类进行解压的操作
        • // 解压文件 并删除文件
                      if (ZipUtils.unzipFile(zipDirectory.getAbsolutePath(),
                              CURRENT.equals(type) ? FilePathUtil.getHuaShengHomeTabImgCurrentDirectory(getApplicationContext())
                                      : FilePathUtil.getHuaShengHomeTabImgNextDirectory(getApplicationContext()))) {
          
                          // 保存文件解压地址
                          saveFileDirPath(homeTabImageInfoBean, type,
                                  CURRENT.equals(type) ? FilePathUtil.getHuaShengHomeTabImgCurrentDirectory(getApplicationContext())
                                          : FilePathUtil.getHuaShengHomeTabImgNextDirectory(getApplicationContext()));
          
                          LogUtils.d("HomeTabImageDownLoadInt", "解压完成---");
          
                      }

      4、其实最关键的就是如何创建并获取我们的文件资源

      • 最重要的就是资源的两种状态切换(选中 or 不选中),通常我们都是使用drawable来写的
      • <?xml version="1.0" encoding="utf-8"?>
        <selector xmlns:android="http://schemas.android.com/apk/res/android">
            <item android:drawable="@mipmap/home_tab_financing_selected" android:state_selected="true" />
            <item android:drawable="@mipmap/home_tab_financing_normal" />
        </selector>
      • 现在我们要根据下载下来的图片(存放在sdcard中)去动态创建drawable,这样我们便能里面系统控件的互斥特性,下面的三个方法代码很重要
      •     // 构建Drawable选择器
            private StateListDrawable createDrawableSelector(Drawable checked, Drawable unchecked) {
                StateListDrawable stateList = new StateListDrawable();
                int state_selected = android.R.attr.state_selected;
                stateList.addState(new int[]{state_selected}, checked);
                stateList.addState(new int[]{-state_selected}, unchecked);
                return stateList;
            }
            // 构建颜色选择器
            private ColorStateList createColorSelector(int checkedColor, int uncheckedColor) {
        
                return new ColorStateList(
                        new int[][]{new int[]{android.R.attr.state_selected},
                                new int[]{-android.R.attr.state_selected}},
                        new int[]{checkedColor, uncheckedColor});
            }
            // 将文件转换成Drawable
            // pathName就是图片存放的绝对路径
            private Drawable getDrawableByFile(String pathName) {
                return Drawable.createFromPath(pathName);
            }

      5、如何给TabLayout设置上资源我觉得就不需要我来说了吧

      • 取出TabLayout的所有的Tab,遍历,然后根据特定条件去设置相应的drawable

    好了,这样就可以实现底部Tab动态替换了,最后奉上一个解压的工具类

      1 import com.blankj.utilcode.util.CloseUtils;
      2 import com.blankj.utilcode.util.StringUtils;
      3 
      4 import java.io.BufferedInputStream;
      5 import java.io.BufferedOutputStream;
      6 import java.io.File;
      7 import java.io.FileInputStream;
      8 import java.io.FileOutputStream;
      9 import java.io.IOException;
     10 import java.io.InputStream;
     11 import java.io.OutputStream;
     12 import java.util.ArrayList;
     13 import java.util.Collection;
     14 import java.util.Enumeration;
     15 import java.util.List;
     16 import java.util.zip.ZipEntry;
     17 import java.util.zip.ZipFile;
     18 import java.util.zip.ZipOutputStream;
     19 
     20 /**
     21  * <pre>
     22  *     author: Blankj
     23  *     blog  : http://blankj.com
     24  *     time  : 2016/08/27
     25  *     desc  : 压缩相关工具类
     26  * </pre>
     27  */
     28 public final class ZipUtils {
     29 
     30     private static final int KB = 1024;
     31 
     32     private ZipUtils() {
     33         throw new UnsupportedOperationException("u can't instantiate me...");
     34     }
     35 
     36     /**
     37      * 批量压缩文件
     38      *
     39      * @param resFiles    待压缩文件集合
     40      * @param zipFilePath 压缩文件路径
     41      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
     42      * @throws IOException IO错误时抛出
     43      */
     44     public static boolean zipFiles(Collection<File> resFiles, String zipFilePath)
     45             throws IOException {
     46         return zipFiles(resFiles, zipFilePath, null);
     47     }
     48 
     49     /**
     50      * 批量压缩文件
     51      *
     52      * @param resFiles    待压缩文件集合
     53      * @param zipFilePath 压缩文件路径
     54      * @param comment     压缩文件的注释
     55      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
     56      * @throws IOException IO错误时抛出
     57      */
     58     public static boolean zipFiles(Collection<File> resFiles, String zipFilePath, String comment)
     59             throws IOException {
     60         return zipFiles(resFiles, FileUtils.getFileByPath(zipFilePath), comment);
     61     }
     62 
     63     /**
     64      * 批量压缩文件
     65      *
     66      * @param resFiles 待压缩文件集合
     67      * @param zipFile  压缩文件
     68      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
     69      * @throws IOException IO错误时抛出
     70      */
     71     public static boolean zipFiles(Collection<File> resFiles, File zipFile)
     72             throws IOException {
     73         return zipFiles(resFiles, zipFile, null);
     74     }
     75 
     76     /**
     77      * 批量压缩文件
     78      *
     79      * @param resFiles 待压缩文件集合
     80      * @param zipFile  压缩文件
     81      * @param comment  压缩文件的注释
     82      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
     83      * @throws IOException IO错误时抛出
     84      */
     85     public static boolean zipFiles(Collection<File> resFiles, File zipFile, String comment)
     86             throws IOException {
     87         if (resFiles == null || zipFile == null) return false;
     88         ZipOutputStream zos = null;
     89         try {
     90             zos = new ZipOutputStream(new FileOutputStream(zipFile));
     91             for (File resFile : resFiles) {
     92                 if (!zipFile(resFile, "", zos, comment)) return false;
     93             }
     94             return true;
     95         } finally {
     96             if (zos != null) {
     97                 zos.finish();
     98                 CloseUtils.closeIO(zos);
     99             }
    100         }
    101     }
    102 
    103     /**
    104      * 压缩文件
    105      *
    106      * @param resFilePath 待压缩文件路径
    107      * @param zipFilePath 压缩文件路径
    108      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
    109      * @throws IOException IO错误时抛出
    110      */
    111     public static boolean zipFile(String resFilePath, String zipFilePath)
    112             throws IOException {
    113         return zipFile(resFilePath, zipFilePath, null);
    114     }
    115 
    116     /**
    117      * 压缩文件
    118      *
    119      * @param resFilePath 待压缩文件路径
    120      * @param zipFilePath 压缩文件路径
    121      * @param comment     压缩文件的注释
    122      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
    123      * @throws IOException IO错误时抛出
    124      */
    125     public static boolean zipFile(String resFilePath, String zipFilePath, String comment)
    126             throws IOException {
    127         return zipFile(FileUtils.getFileByPath(resFilePath), FileUtils.getFileByPath(zipFilePath), comment);
    128     }
    129 
    130     /**
    131      * 压缩文件
    132      *
    133      * @param resFile 待压缩文件
    134      * @param zipFile 压缩文件
    135      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
    136      * @throws IOException IO错误时抛出
    137      */
    138     public static boolean zipFile(File resFile, File zipFile)
    139             throws IOException {
    140         return zipFile(resFile, zipFile, null);
    141     }
    142 
    143     /**
    144      * 压缩文件
    145      *
    146      * @param resFile 待压缩文件
    147      * @param zipFile 压缩文件
    148      * @param comment 压缩文件的注释
    149      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
    150      * @throws IOException IO错误时抛出
    151      */
    152     public static boolean zipFile(File resFile, File zipFile, String comment)
    153             throws IOException {
    154         if (resFile == null || zipFile == null) return false;
    155         ZipOutputStream zos = null;
    156         try {
    157             zos = new ZipOutputStream(new FileOutputStream(zipFile));
    158             return zipFile(resFile, "", zos, comment);
    159         } finally {
    160             if (zos != null) {
    161                 CloseUtils.closeIO(zos);
    162             }
    163         }
    164     }
    165 
    166     /**
    167      * 压缩文件
    168      *
    169      * @param resFile  待压缩文件
    170      * @param rootPath 相对于压缩文件的路径
    171      * @param zos      压缩文件输出流
    172      * @param comment  压缩文件的注释
    173      * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
    174      * @throws IOException IO错误时抛出
    175      */
    176     private static boolean zipFile(File resFile, String rootPath, ZipOutputStream zos, String comment)
    177             throws IOException {
    178         rootPath = rootPath + (isSpace(rootPath) ? "" : File.separator) + resFile.getName();
    179         if (resFile.isDirectory()) {
    180             File[] fileList = resFile.listFiles();
    181             // 如果是空文件夹那么创建它,我把'/'换为File.separator测试就不成功,eggPain
    182             if (fileList == null || fileList.length <= 0) {
    183                 ZipEntry entry = new ZipEntry(rootPath + '/');
    184                 if (!StringUtils.isEmpty(comment)) entry.setComment(comment);
    185                 zos.putNextEntry(entry);
    186                 zos.closeEntry();
    187             } else {
    188                 for (File file : fileList) {
    189                     // 如果递归返回false则返回false
    190                     if (!zipFile(file, rootPath, zos, comment)) return false;
    191                 }
    192             }
    193         } else {
    194             InputStream is = null;
    195             try {
    196                 is = new BufferedInputStream(new FileInputStream(resFile));
    197                 ZipEntry entry = new ZipEntry(rootPath);
    198                 if (!StringUtils.isEmpty(comment)) entry.setComment(comment);
    199                 zos.putNextEntry(entry);
    200                 byte buffer[] = new byte[KB];
    201                 int len;
    202                 while ((len = is.read(buffer, 0, KB)) != -1) {
    203                     zos.write(buffer, 0, len);
    204                 }
    205                 zos.closeEntry();
    206             } finally {
    207                 CloseUtils.closeIO(is);
    208             }
    209         }
    210         return true;
    211     }
    212 
    213     /**
    214      * 批量解压文件
    215      *
    216      * @param zipFiles    压缩文件集合
    217      * @param destDirPath 目标目录路径
    218      * @return {@code true}: 解压成功<br>{@code false}: 解压失败
    219      * @throws IOException IO错误时抛出
    220      */
    221     public static boolean unzipFiles(Collection<File> zipFiles, String destDirPath)
    222             throws IOException {
    223         return unzipFiles(zipFiles, FileUtils.getFileByPath(destDirPath));
    224     }
    225 
    226     /**
    227      * 批量解压文件
    228      *
    229      * @param zipFiles 压缩文件集合
    230      * @param destDir  目标目录
    231      * @return {@code true}: 解压成功<br>{@code false}: 解压失败
    232      * @throws IOException IO错误时抛出
    233      */
    234     public static boolean unzipFiles(Collection<File> zipFiles, File destDir)
    235             throws IOException {
    236         if (zipFiles == null || destDir == null) return false;
    237         for (File zipFile : zipFiles) {
    238             if (!unzipFile(zipFile, destDir)) return false;
    239         }
    240         return true;
    241     }
    242 
    243     /**
    244      * 解压文件
    245      *
    246      * @param zipFilePath 待解压文件路径
    247      * @param destDirPath 目标目录路径
    248      * @return {@code true}: 解压成功<br>{@code false}: 解压失败
    249      * @throws IOException IO错误时抛出
    250      */
    251     public static boolean unzipFile(String zipFilePath, String destDirPath) throws IOException {
    252         // 判断是否存在这个路径,没有的话就创建这个路径
    253         File tempDir = new File(destDirPath);
    254         if (!tempDir.exists()) {
    255             tempDir.mkdirs();
    256         }
    257         return unzipFile(FileUtils.getFileByPath(zipFilePath), FileUtils.getFileByPath(destDirPath));
    258     }
    259 
    260     /**
    261      * 解压文件
    262      *
    263      * @param zipFile 待解压文件
    264      * @param destDir 目标目录
    265      * @return {@code true}: 解压成功<br>{@code false}: 解压失败
    266      * @throws IOException IO错误时抛出
    267      */
    268     public static boolean unzipFile(File zipFile, File destDir)
    269             throws IOException {
    270         return unzipFileByKeyword(zipFile, destDir, null) != null;
    271     }
    272 
    273     /**
    274      * 解压带有关键字的文件
    275      *
    276      * @param zipFilePath 待解压文件路径
    277      * @param destDirPath 目标目录路径
    278      * @param keyword     关键字
    279      * @return 返回带有关键字的文件链表
    280      * @throws IOException IO错误时抛出
    281      */
    282     public static List<File> unzipFileByKeyword(String zipFilePath, String destDirPath, String keyword)
    283             throws IOException {
    284         return unzipFileByKeyword(FileUtils.getFileByPath(zipFilePath),
    285                 FileUtils.getFileByPath(destDirPath), keyword);
    286     }
    287 
    288     /**
    289      * 解压带有关键字的文件
    290      *
    291      * @param zipFile 待解压文件
    292      * @param destDir 目标目录
    293      * @param keyword 关键字
    294      * @return 返回带有关键字的文件链表
    295      * @throws IOException IO错误时抛出
    296      */
    297     public static List<File> unzipFileByKeyword(File zipFile, File destDir, String keyword)
    298             throws IOException {
    299         if (zipFile == null || destDir == null) return null;
    300         List<File> files = new ArrayList<>();
    301         ZipFile zf = new ZipFile(zipFile);
    302         Enumeration<?> entries = zf.entries();
    303         while (entries.hasMoreElements()) {
    304             ZipEntry entry = ((ZipEntry) entries.nextElement());
    305             String entryName = entry.getName();
    306             if (StringUtils.isEmpty(keyword) || FileUtils.getFileName(entryName).toLowerCase().contains(keyword.toLowerCase())) {
    307                 String filePath = destDir + File.separator + entryName;
    308                 File file = new File(filePath);
    309                 files.add(file);
    310                 if (entry.isDirectory()) {
    311                     if (!FileUtils.createOrExistsDir(file)) return null;
    312                 } else {
    313                     if (!FileUtils.createOrExistsFile(file)) return null;
    314                     InputStream in = null;
    315                     OutputStream out = null;
    316                     try {
    317                         in = new BufferedInputStream(zf.getInputStream(entry));
    318                         out = new BufferedOutputStream(new FileOutputStream(file));
    319                         byte buffer[] = new byte[KB];
    320                         int len;
    321                         while ((len = in.read(buffer)) != -1) {
    322                             out.write(buffer, 0, len);
    323                         }
    324                     } finally {
    325                         CloseUtils.closeIO(in, out);
    326                     }
    327                 }
    328             }
    329         }
    330         return files;
    331     }
    332 
    333     /**
    334      * 获取压缩文件中的文件路径链表
    335      *
    336      * @param zipFilePath 压缩文件路径
    337      * @return 压缩文件中的文件路径链表
    338      * @throws IOException IO错误时抛出
    339      */
    340     public static List<String> getFilesPath(String zipFilePath)
    341             throws IOException {
    342         return getFilesPath(FileUtils.getFileByPath(zipFilePath));
    343     }
    344 
    345     /**
    346      * 获取压缩文件中的文件路径链表
    347      *
    348      * @param zipFile 压缩文件
    349      * @return 压缩文件中的文件路径链表
    350      * @throws IOException IO错误时抛出
    351      */
    352     public static List<String> getFilesPath(File zipFile)
    353             throws IOException {
    354         if (zipFile == null) return null;
    355         List<String> paths = new ArrayList<>();
    356         Enumeration<?> entries = getEntries(zipFile);
    357         while (entries.hasMoreElements()) {
    358             paths.add(((ZipEntry) entries.nextElement()).getName());
    359         }
    360         return paths;
    361     }
    362 
    363     /**
    364      * 获取压缩文件中的注释链表
    365      *
    366      * @param zipFilePath 压缩文件路径
    367      * @return 压缩文件中的注释链表
    368      * @throws IOException IO错误时抛出
    369      */
    370     public static List<String> getComments(String zipFilePath)
    371             throws IOException {
    372         return getComments(FileUtils.getFileByPath(zipFilePath));
    373     }
    374 
    375     /**
    376      * 获取压缩文件中的注释链表
    377      *
    378      * @param zipFile 压缩文件
    379      * @return 压缩文件中的注释链表
    380      * @throws IOException IO错误时抛出
    381      */
    382     public static List<String> getComments(File zipFile)
    383             throws IOException {
    384         if (zipFile == null) return null;
    385         List<String> comments = new ArrayList<>();
    386         Enumeration<?> entries = getEntries(zipFile);
    387         while (entries.hasMoreElements()) {
    388             ZipEntry entry = ((ZipEntry) entries.nextElement());
    389             comments.add(entry.getComment());
    390         }
    391         return comments;
    392     }
    393 
    394     /**
    395      * 获取压缩文件中的文件对象
    396      *
    397      * @param zipFilePath 压缩文件路径
    398      * @return 压缩文件中的文件对象
    399      * @throws IOException IO错误时抛出
    400      */
    401     public static Enumeration<?> getEntries(String zipFilePath)
    402             throws IOException {
    403         return getEntries(FileUtils.getFileByPath(zipFilePath));
    404     }
    405 
    406     /**
    407      * 获取压缩文件中的文件对象
    408      *
    409      * @param zipFile 压缩文件
    410      * @return 压缩文件中的文件对象
    411      * @throws IOException IO错误时抛出
    412      */
    413     public static Enumeration<?> getEntries(File zipFile)
    414             throws IOException {
    415         if (zipFile == null) return null;
    416         return new ZipFile(zipFile).entries();
    417     }
    418 
    419     private static boolean isSpace(String s) {
    420         if (s == null) return true;
    421         for (int i = 0, len = s.length(); i < len; ++i) {
    422             if (!Character.isWhitespace(s.charAt(i))) {
    423                 return false;
    424             }
    425         }
    426         return true;
    427     }
    428 }
    工具类
  • 相关阅读:
    关于位运算(转)
    计蒜客第三场
    数组与指针
    计蒜客第二场
    指针概念
    爬楼梯(动态规划)
    线性表基本操作的实现(合并)
    4123=喵帕斯之天才少女
    3889=神奇的函数
    1586=计算组合数
  • 原文地址:https://www.cnblogs.com/819158327fan/p/9304888.html
Copyright © 2020-2023  润新知