Dynamic Replacement of the Bottom Navigation Bar of Android

  • 2021-10-25 07:55:18
  • OfStack

The dynamic replacement scheme of the navigation bar at the bottom of Android is for your reference, and the specific contents are as follows

1. Generally speaking, under 1 general circumstances, the BottomTab of our app will have the following implementation methods.

1), customize view, and then write your own logic to achieve mutual exclusion.

2) Use RadioGroup + RadioButton to implement the bottom Tab.
The degree of freedom ratio is extremely high, so you can rewrite RadioButton if you want to achieve complexity.

3), using TabLayout in google design package.
It can go up, down and slide
If you are lazy, you can set up some resources according to the existing api, or you can use setCustomView ()

4), using BottomNavigationView in google design package.

(1) Use menu to set up resources
(2) There is a default animation effect

2. This article introduces the similar method of dynamically replacing the bottom navigation resource picture according to the background distribution in JD.COM and Taobao (based on TabLayout)
Since it is mentioned that dynamic replacement definitely means downloading resources, let's talk about IntentService first

IntentService is also an service, but google helps us encapsulate and maintain an HandlerThread, and the operations in it are asynchronous. When the task is finished, IntentService will stop automatically, so we don't need to finish it manually. If IntentService is started many times, every time-consuming operation will be executed in the onHandleIntent callback method of IntentService in the form of work queue, and will be executed in turn, using serial mode, and will automatically end after execution.

onHandlerIntent (Intent intent) is the most important method


@Override
 protected void onHandleIntent(Intent intent) {
  if (intent != null) {
   final String action = intent.getAction();
   if (ACTION_FOO.equals(action)) {
    //  In which time-consuming tasks are processed, and when all time-consuming tasks are finished, IntentService It will be automatic finish Drop, don't need developers to care. 
   }
  }
 }

The reason for choosing IntentService is that the following operations are time-consuming operations, so we simply encapsulate them into service, and we only need to start this Service at the right time to be ok

Need to download resource compression package Because it is dynamic replacement, it must involve pre-download, so the data format should be set first (the following is the data format).

{
  "currentInfo":{// Current style 
   "id":"111",
   "imageZipUrl": Your download address ,
   "tabNamesList":[
     " Home page 1"," Nearby 1"," Discover 1"," Mine 1"
   ],
   "tabColorNormal":"B0C4DE",
   "tabColorHighlight":"F7B62D",
   "startTime": Start time ,
   "deadLineTime": End time 
  },
  "nextInfo":{// Under 1 Style of secondary display 
   "id":"111",
   "imageZipUrl": Your download address ,
   "tabNamesList":[
     " Home page 2"," Nearby 2"," Discover 2"," Mine 2"
   ],
   "tabColorNormal":"B0C4DE",
   "tabColorHighlight":"FE6246",
   "startTime": Start time ,
   "deadLineTime": End time 
  }
 }
Need to store resource compression package

Code for downloading and storing files (Retrofit is used here for downloading)


//  Download a file 
  Response<ResponseBody> zipFile = ServiceGenerator.createService(HomeService.class)
   .downloadFileRetrofit(getFileDownLoadUrl(homeTabImageInfoBean, type))
   .execute();

   //  Get the file stream 
   ResponseBody zipBody = zipFile.body();

   LogUtils.d("DownLoad", " Download complete ");

   //  Create 1 Files 
   File zipDirectory = new File(FilePathUtil.getHuaShengHomeTabZipDirectory(getApplicationContext())
     + createZipFileName(homeTabImageInfoBean, type));

   //  If the file does not exist, create a folder 
   if (!zipDirectory.exists()) {
    zipDirectory.createNewFile();
   }

   //  Save a file 
   FileUtils.writeFile2Disk(zipBody, zipDirectory);
Extract the resource and delete the file (the extraction method is written at the bottom of the article because it is too long)

//  Extract a file   And delete the file 
   if (ZipUtils.unzipFile(zipDirectory.getAbsolutePath(),
     CURRENT.equals(type) ? FilePathUtil.getHuaShengHomeTabImgCurrentDirectory(getApplicationContext())
       : FilePathUtil.getHuaShengHomeTabImgNextDirectory(getApplicationContext()))) {

    //  Save the unzipped address of the file 
    saveFileDirPath(homeTabImageInfoBean, type,
      CURRENT.equals(type) ? FilePathUtil.getHuaShengHomeTabImgCurrentDirectory(getApplicationContext())
        : FilePathUtil.getHuaShengHomeTabImgNextDirectory(getApplicationContext()));

    LogUtils.d("HomeTabImageDownLoadInt", " Decompression complete ---");

   }

In fact, the most important thing is how to create and obtain our file resources

It is important to switch between the two states of the resource (or is selected but not selected). Usually, we use drawable to write


<?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>

Now we need to dynamically create drawable based on the downloaded images (stored in sdcard) so that we can have the mutual exclusion of system controls inside

The following three method codes are important


//  Build Drawable Selector 
 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;
 }

//  Build a color picker 
 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});

//  Convert a file to Drawable
 // pathName It is the absolute path where pictures are stored 
 private Drawable getDrawableByFile(String pathName) {
  return Drawable.createFromPath(pathName);
 }

Finally, set up resources on tab of TabLayout

Take out all Tab of TabLayout, traverse, and then set the corresponding drawable according to specific conditions

Finally, at the end of this article, we attach the above compression related tool class


import com.blankj.utilcode.util.CloseUtils;
import com.blankj.utilcode.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * <pre>
 *  author:  Cheng Long 
 *  time : 2018/12/14
 *  desc :  Compression related tool classes 
 * </pre>
 */
public final class ZipUtils {

 private static final int KB = 1024;

 private ZipUtils() {
  throw new UnsupportedOperationException("u can't instantiate me...");
 }

 /**
  *  Batch compressed files 
  *
  * @param resFiles  Collection of files to be compressed 
  * @param zipFilePath  Compressed file path 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFiles(Collection<File> resFiles, String zipFilePath)
   throws IOException {
  return zipFiles(resFiles, zipFilePath, null);
 }

 /**
  *  Batch compressed files 
  *
  * @param resFiles  Collection of files to be compressed 
  * @param zipFilePath  Compressed file path 
  * @param comment   Comments on compressed files 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFiles(Collection<File> resFiles, String zipFilePath, String comment)
   throws IOException {
  return zipFiles(resFiles, FileUtils.getFileByPath(zipFilePath), comment);
 }

 /**
  *  Batch compressed files 
  *
  * @param resFiles  Collection of files to be compressed 
  * @param zipFile  Zip file 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFiles(Collection<File> resFiles, File zipFile)
   throws IOException {
  return zipFiles(resFiles, zipFile, null);
 }

 /**
  *  Batch compressed files 
  *
  * @param resFiles  Collection of files to be compressed 
  * @param zipFile  Zip file 
  * @param comment  Comments on compressed files 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFiles(Collection<File> resFiles, File zipFile, String comment)
   throws IOException {
  if (resFiles == null || zipFile == null) return false;
  ZipOutputStream zos = null;
  try {
   zos = new ZipOutputStream(new FileOutputStream(zipFile));
   for (File resFile : resFiles) {
    if (!zipFile(resFile, "", zos, comment)) return false;
   }
   return true;
  } finally {
   if (zos != null) {
    zos.finish();
    CloseUtils.closeIO(zos);
   }
  }
 }

 /**
  *  Zip file 
  *
  * @param resFilePath  Path of file to be compressed 
  * @param zipFilePath  Compressed file path 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFile(String resFilePath, String zipFilePath)
   throws IOException {
  return zipFile(resFilePath, zipFilePath, null);
 }

 /**
  *  Zip file 
  *
  * @param resFilePath  Path of file to be compressed 
  * @param zipFilePath  Compressed file path 
  * @param comment   Comments on compressed files 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFile(String resFilePath, String zipFilePath, String comment)
   throws IOException {
  return zipFile(FileUtils.getFileByPath(resFilePath), FileUtils.getFileByPath(zipFilePath), comment);
 }

 /**
  *  Zip file 
  *
  * @param resFile  File to be compressed 
  * @param zipFile  Zip file 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFile(File resFile, File zipFile)
   throws IOException {
  return zipFile(resFile, zipFile, null);
 }

 /**
  *  Zip file 
  *
  * @param resFile  File to be compressed 
  * @param zipFile  Zip file 
  * @param comment  Comments on compressed files 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean zipFile(File resFile, File zipFile, String comment)
   throws IOException {
  if (resFile == null || zipFile == null) return false;
  ZipOutputStream zos = null;
  try {
   zos = new ZipOutputStream(new FileOutputStream(zipFile));
   return zipFile(resFile, "", zos, comment);
  } finally {
   if (zos != null) {
    CloseUtils.closeIO(zos);
   }
  }
 }

 /**
  *  Zip file 
  *
  * @param resFile  File to be compressed 
  * @param rootPath  Path relative to compressed file 
  * @param zos   Compressed file output stream 
  * @param comment  Comments on compressed files 
  * @return {@code true}:  Compression succeeded <br>{@code false}:  Compression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 private static boolean zipFile(File resFile, String rootPath, ZipOutputStream zos, String comment)
   throws IOException {
  rootPath = rootPath + (isSpace(rootPath) ? "" : File.separator) + resFile.getName();
  if (resFile.isDirectory()) {
   File[] fileList = resFile.listFiles();
   //  If it is an empty folder, then create it, and I put '/' For File.separator The test is unsuccessful, eggPain
   if (fileList == null || fileList.length <= 0) {
    ZipEntry entry = new ZipEntry(rootPath + '/');
    if (!StringUtils.isEmpty(comment)) entry.setComment(comment);
    zos.putNextEntry(entry);
    zos.closeEntry();
   } else {
    for (File file : fileList) {
     //  If the recursion returns false Returns the false
     if (!zipFile(file, rootPath, zos, comment)) return false;
    }
   }
  } else {
   InputStream is = null;
   try {
    is = new BufferedInputStream(new FileInputStream(resFile));
    ZipEntry entry = new ZipEntry(rootPath);
    if (!StringUtils.isEmpty(comment)) entry.setComment(comment);
    zos.putNextEntry(entry);
    byte buffer[] = new byte[KB];
    int len;
    while ((len = is.read(buffer, 0, KB)) != -1) {
     zos.write(buffer, 0, len);
    }
    zos.closeEntry();
   } finally {
    CloseUtils.closeIO(is);
   }
  }
  return true;
 }

 /**
  *  Batch unzip file 
  *
  * @param zipFiles  Compressed file collection 
  * @param destDirPath  Destination directory path 
  * @return {@code true}:  Successful decompression <br>{@code false}:  Decompression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean unzipFiles(Collection<File> zipFiles, String destDirPath)
   throws IOException {
  return unzipFiles(zipFiles, FileUtils.getFileByPath(destDirPath));
 }

 /**
  *  Batch unzip file 
  *
  * @param zipFiles  Compressed file collection 
  * @param destDir  Target directory 
  * @return {@code true}:  Successful decompression <br>{@code false}:  Decompression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean unzipFiles(Collection<File> zipFiles, File destDir)
   throws IOException {
  if (zipFiles == null || destDir == null) return false;
  for (File zipFile : zipFiles) {
   if (!unzipFile(zipFile, destDir)) return false;
  }
  return true;
 }

 /**
  *  Extract a file 
  *
  * @param zipFilePath  Path of file to be unzipped 
  * @param destDirPath  Destination directory path 
  * @return {@code true}:  Successful decompression <br>{@code false}:  Decompression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean unzipFile(String zipFilePath, String destDirPath) throws IOException {
  //  Determine whether this path exists , If not, create this path 
  File tempDir = new File(destDirPath);
  if (!tempDir.exists()) {
   tempDir.mkdirs();
  }
  return unzipFile(FileUtils.getFileByPath(zipFilePath), FileUtils.getFileByPath(destDirPath));
 }

 /**
  *  Extract a file 
  *
  * @param zipFile  File to be unzipped 
  * @param destDir  Target directory 
  * @return {@code true}:  Successful decompression <br>{@code false}:  Decompression failed 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static boolean unzipFile(File zipFile, File destDir)
   throws IOException {
  return unzipFileByKeyword(zipFile, destDir, null) != null;
 }

 /**
  *  Unzip a file with a keyword 
  *
  * @param zipFilePath  Path of file to be unzipped 
  * @param destDirPath  Destination directory path 
  * @param keyword   Keyword 
  * @return  Returns a linked list of files with keywords 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<File> unzipFileByKeyword(String zipFilePath, String destDirPath, String keyword)
   throws IOException {
  return unzipFileByKeyword(FileUtils.getFileByPath(zipFilePath),
    FileUtils.getFileByPath(destDirPath), keyword);
 }

 /**
  *  Unzip a file with a keyword 
  *
  * @param zipFile  File to be unzipped 
  * @param destDir  Target directory 
  * @param keyword  Keyword 
  * @return  Returns a linked list of files with keywords 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<File> unzipFileByKeyword(File zipFile, File destDir, String keyword)
   throws IOException {
  if (zipFile == null || destDir == null) return null;
  List<File> files = new ArrayList<>();
  ZipFile zf = new ZipFile(zipFile);
  Enumeration<?> entries = zf.entries();
  while (entries.hasMoreElements()) {
   ZipEntry entry = ((ZipEntry) entries.nextElement());
   String entryName = entry.getName();
   if (StringUtils.isEmpty(keyword) || FileUtils.getFileName(entryName).toLowerCase().contains(keyword.toLowerCase())) {
    String filePath = destDir + File.separator + entryName;
    File file = new File(filePath);
    files.add(file);
    if (entry.isDirectory()) {
     if (!FileUtils.createOrExistsDir(file)) return null;
    } else {
     if (!FileUtils.createOrExistsFile(file)) return null;
     InputStream in = null;
     OutputStream out = null;
     try {
      in = new BufferedInputStream(zf.getInputStream(entry));
      out = new BufferedOutputStream(new FileOutputStream(file));
      byte buffer[] = new byte[KB];
      int len;
      while ((len = in.read(buffer)) != -1) {
       out.write(buffer, 0, len);
      }
     } finally {
      CloseUtils.closeIO(in, out);
     }
    }
   }
  }
  return files;
 }

 /**
  *  Gets a linked list of file paths in a compressed file 
  *
  * @param zipFilePath  Compressed file path 
  * @return  Linked list of file paths in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<String> getFilesPath(String zipFilePath)
   throws IOException {
  return getFilesPath(FileUtils.getFileByPath(zipFilePath));
 }

 /**
  *  Gets a linked list of file paths in a compressed file 
  *
  * @param zipFile  Zip file 
  * @return  Linked list of file paths in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<String> getFilesPath(File zipFile)
   throws IOException {
  if (zipFile == null) return null;
  List<String> paths = new ArrayList<>();
  Enumeration<?> entries = getEntries(zipFile);
  while (entries.hasMoreElements()) {
   paths.add(((ZipEntry) entries.nextElement()).getName());
  }
  return paths;
 }

 /**
  *  Get a linked list of comments in a compressed file 
  *
  * @param zipFilePath  Compressed file path 
  * @return  Linked list of comments in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<String> getComments(String zipFilePath)
   throws IOException {
  return getComments(FileUtils.getFileByPath(zipFilePath));
 }

 /**
  *  Get a linked list of comments in a compressed file 
  *
  * @param zipFile  Zip file 
  * @return  Linked list of comments in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static List<String> getComments(File zipFile)
   throws IOException {
  if (zipFile == null) return null;
  List<String> comments = new ArrayList<>();
  Enumeration<?> entries = getEntries(zipFile);
  while (entries.hasMoreElements()) {
   ZipEntry entry = ((ZipEntry) entries.nextElement());
   comments.add(entry.getComment());
  }
  return comments;
 }

 /**
  *  Get the file object in the zip file 
  *
  * @param zipFilePath  Compressed file path 
  * @return  File objects in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static Enumeration<?> getEntries(String zipFilePath)
   throws IOException {
  return getEntries(FileUtils.getFileByPath(zipFilePath));
 }

 /**
  *  Get the file object in the zip file 
  *
  * @param zipFile  Zip file 
  * @return  File objects in compressed files 
  * @throws IOException IO Thrown when an error occurs 
  */
 public static Enumeration<?> getEntries(File zipFile)
   throws IOException {
  if (zipFile == null) return null;
  return new ZipFile(zipFile).entries();
 }

 private static boolean isSpace(String s) {
  if (s == null) return true;
  for (int i = 0, len = s.length(); i < len; ++i) {
   if (!Character.isWhitespace(s.charAt(i))) {
    return false;
   }
  }
  return true;
 }
}


Related articles: