1.首先看一下效果

1.1做成了一个GIF

  

1.2.我用格式工厂有点问题,大小无法调到手机这样的大小,目前还没有解决方案。

1.3.网上有免费的MP4->GIF,参考一下这个网站吧。

1.4.讲解一下这个图片吧,首先是从话题中点击了其中一张图片,进入图片Activity,

   然后可以自由放大,自由翻转。

2.分析一下继承的BaseImageActivity

2.1因为ImageActivity继承了BaseImageActivtiy,首先看看源代码。

  1. /*
  2. * Copyright 2017 GcsSloop
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Last modified 2017-03-15 20:02:59
  17. *
  18. * GitHub: https://github.com/GcsSloop
  19. * Website: http://www.gcssloop.com
  20. * Weibo: http://weibo.com/GcsSloop
  21. */
  22.  
  23. package com.gcssloop.diycode.base.app;
  24.  
  25. import android.content.Intent;
  26.  
  27. import java.util.ArrayList;
  28.  
  29. /**
  30. * 对数据进行预处理
  31. */
  32. public abstract class BaseImageActivity extends BaseActivity {
  33. public static final String ALL_IMAGE_URLS = "all_images";
  34. public static final String CURRENT_IMAGE_URL = "current_image";
  35.  
  36. protected static final String MODE_NORMAL = "normal";
  37. protected static final String MODE_ERROR = "error";
  38. protected String mCurrentMode = MODE_NORMAL;
  39.  
  40. protected ArrayList<String> images = new ArrayList<>(); // 所有图片
  41. protected String current_image_url = null; // 当前图片
  42. protected int current_image_position = 0; // 当前图片位置
  43.  
  44. @Override
  45. protected void initDatas() {
  46. super.initDatas();
  47.  
  48. // 初始化图片 url 和 图片集合,保证两个数据都存在
  49. Intent intent = getIntent();
  50.  
  51. // 没有传递当前图片,设置模式为错误
  52. String imageUrl = intent.getStringExtra(CURRENT_IMAGE_URL);
  53. if (imageUrl == null || imageUrl.isEmpty()) {
  54. toastShort("没有传递图片链接");
  55. mCurrentMode = MODE_ERROR;
  56. return;
  57. }
  58. mCurrentMode = MODE_NORMAL;
  59.  
  60. ArrayList<String> temp = intent.getStringArrayListExtra(ALL_IMAGE_URLS);
  61. if (temp == null || temp.size() <= 0) {
  62. // 记录当前图片,计算位置
  63. images.clear();
  64. images.add(imageUrl);
  65. } else if (temp.size() > 0) {
  66. // 如果图片集合大于0
  67. images = new ArrayList<>(temp);
  68. }
  69.  
  70. if (!images.contains(imageUrl)) {
  71. images.add(imageUrl);
  72. }
  73.  
  74. current_image_url = imageUrl;
  75. current_image_position = images.indexOf(current_image_url);
  76. }
  77. }

2.2这里需要回顾一下BaseActivity。

  BaseActivity==>主要提供了项目本身API提供的Diycode类,ViewHolder类,Toast类,两个抽象方法。ActionBar。结束

当前活动。

打开活动。

2.3.私有成员变量。

  ·存放图片链接的一个ArrayList<String>

  ·存放当前图片的链接current_image_url

  ·存放当前图片位置,整型current_image_positon

2.4.注意点,Override一个在基类中已经实现过的函数。

  重载,也是可以Override的。以前理解的Override就是抽象方法,接口方法。这里又涨知识了。

  如果写了一个父类中有的方法,就相当于重载了父类中的方法,就不会执行父类中的那个方法,而是执行自己的方法了。

名字是一样的。

2.5.在BaseActivity中写了一个空的initDatas方法。而且规定了这个方法执行的顺序。

  然后在子类BaseImageActivity中重载这个方法。

  这个类主要处理图片数据的。

2.6分析这个重载方法。

  

  

  2.6.1首先从intent中获得当前图片,如果没有,设置模式为错误。否则,设置模式为正常。

  2.6.2然后从intent中获得所有图片集合,如果没有,就把当前图片加进images去,如果有,就将集合加到一个images中。

  2.6.3然后它还判断了一下images中是否包含了当前url,应该是双保险吧,感觉没必要。

  2.6.4最后,就把当前图片和当前位置更新了一下。

3.分析一下图片缓存DiskImageCache

3.1首先看看在ImageActivity中定义的一个变量。DiskImageCache mCache;源代码如下:

  1. /*
  2. * Copyright 2017 GcsSloop
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Last modified 2017-03-12 00:56:52
  17. *
  18. * GitHub: https://github.com/GcsSloop
  19. * Website: http://www.gcssloop.com
  20. * Weibo: http://weibo.com/GcsSloop
  21. */
  22.  
  23. package com.gcssloop.diycode.base.webview;
  24.  
  25. import android.content.Context;
  26. import android.graphics.Bitmap;
  27. import android.graphics.BitmapFactory;
  28. import android.os.Environment;
  29. import android.os.StatFs;
  30. import android.support.annotation.NonNull;
  31. import android.util.Log;
  32.  
  33. import java.io.File;
  34. import java.io.FileInputStream;
  35. import java.io.FileNotFoundException;
  36. import java.io.FileOutputStream;
  37. import java.io.IOException;
  38. import java.io.OutputStream;
  39. import java.security.MessageDigest;
  40. import java.security.NoSuchAlgorithmException;
  41. import java.util.Arrays;
  42. import java.util.Comparator;
  43.  
  44. public class DiskImageCache {
  45. private static final String CACHE_SUFFIX = ".cache";
  46.  
  47. private static final int MB = 1024 * 1024;
  48. private static final int CACHE_SIZE = 50; // 缓存占用空间大小
  49. private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10; // 为 SD 卡保留多少空间
  50.  
  51. private File cacheDir;
  52.  
  53. public DiskImageCache(Context context) {
  54. cacheDir = getDiskCacheDir(context, "web-image");
  55. // 整理缓存
  56. organizeCache(cacheDir);
  57. }
  58.  
  59. /**
  60. * 从缓存中获取图片
  61. **/
  62. public Bitmap getBitmap(final String key) {
  63. final String path = getCachePath(key);
  64. File file = new File(path);
  65. if (file.exists()) {
  66. Bitmap bmp = BitmapFactory.decodeFile(path);
  67. if (bmp == null) {
  68. file.delete();
  69. } else {
  70. updateFileTime(path);
  71. return bmp;
  72. }
  73. }
  74. return null;
  75. }
  76.  
  77. /**
  78. * 将图片存入文件缓存
  79. **/
  80. public void saveBitmap(String key, Bitmap bm) {
  81. if (bm == null) {
  82. return;
  83. }
  84. //判断sdcard上的空间
  85. if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  86. return; //SD空间不足
  87. }
  88. File file = new File(getCachePath(key));
  89. try {
  90. file.createNewFile();
  91. OutputStream outStream = new FileOutputStream(file);
  92. bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
  93. outStream.flush();
  94. outStream.close();
  95. } catch (FileNotFoundException e) {
  96. Log.w("ImageFileCache", "FileNotFoundException");
  97. } catch (IOException e) {
  98. Log.w("ImageFileCache", "IOException");
  99. }
  100. }
  101.  
  102. /**
  103. * 保存 bytes 数据
  104. *
  105. * @param key url
  106. * @param bytes bytes 数据
  107. */
  108. public void saveBytes(String key, byte[] bytes) {
  109. if (bytes == null) {
  110. return;
  111. }
  112. //判断sdcard上的空间
  113. if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  114. return; //SD空间不足
  115. }
  116. File file = new File(getCachePath(key));
  117. try {
  118. file.createNewFile();
  119. OutputStream outStream = new FileOutputStream(file);
  120. outStream.write(bytes);
  121. outStream.flush();
  122. outStream.close();
  123. } catch (FileNotFoundException e) {
  124. Log.w("ImageFileCache", "FileNotFoundException");
  125. } catch (IOException e) {
  126. Log.w("ImageFileCache", "IOException");
  127. }
  128. }
  129.  
  130. /**
  131. * 获取一个本地缓存的输入流
  132. *
  133. * @param key url
  134. * @return FileInputStream
  135. */
  136. public FileInputStream getStream(String key) {
  137. File file = new File(getCachePath(key));
  138. if (!file.exists())
  139. return null;
  140. try {
  141. FileInputStream inputStream = new FileInputStream(file);
  142. return inputStream;
  143. } catch (FileNotFoundException e) {
  144. Log.e("getStream", "FileNotFoundException");
  145. e.printStackTrace();
  146. }
  147. return null;
  148. }
  149.  
  150. /**
  151. * 获取本地缓存路径
  152. *
  153. * @param key url
  154. * @return 路径
  155. */
  156. public String getDiskPath(String key) {
  157. File file = new File(getCachePath(key));
  158. if (!file.exists())
  159. return null;
  160. return file.getAbsolutePath();
  161. }
  162.  
  163. /**
  164. * 是否有缓存
  165. *
  166. * @param key url
  167. * @return 是否有缓存
  168. */
  169. public boolean hasCache(String key) {
  170. File file = new File(getCachePath(key));
  171. return file.exists();
  172. }
  173.  
  174. /**
  175. * 计算存储目录下的文件大小,
  176. * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
  177. * 那么删除40%最近没有被使用的文件
  178. */
  179. private boolean organizeCache(@NonNull File cacheDir) {
  180. File[] files = cacheDir.listFiles();
  181. if (files == null) {
  182. return true;
  183. }
  184. if (!Environment.getExternalStorageState().equals(
  185. Environment.MEDIA_MOUNTED)) {
  186. return false;
  187. }
  188.  
  189. int dirSize = 0;
  190. for (int i = 0; i < files.length; i++) {
  191. if (files[i].getName().contains(CACHE_SUFFIX)) {
  192. dirSize += files[i].length();
  193. }
  194. }
  195.  
  196. if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  197. int removeFactor = (int) ((0.4 * files.length) + 1);
  198. Arrays.sort(files, new FileLastModifSort());
  199. for (int i = 0; i < removeFactor; i++) {
  200. if (files[i].getName().contains(CACHE_SUFFIX)) {
  201. files[i].delete();
  202. }
  203. }
  204. }
  205.  
  206. if (freeSpaceOnSd() <= CACHE_SIZE) {
  207. return false;
  208. }
  209.  
  210. return true;
  211. }
  212.  
  213. /**
  214. * 修改文件的最后修改时间
  215. **/
  216. public void updateFileTime(String path) {
  217. File file = new File(path);
  218. long newModifiedTime = System.currentTimeMillis();
  219. file.setLastModified(newModifiedTime);
  220. }
  221.  
  222. /**
  223. * 计算sdcard上的剩余空间
  224. **/
  225. private int freeSpaceOnSd() {
  226. StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
  227. double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
  228. return (int) sdFreeMB;
  229. }
  230.  
  231. /**
  232. * 根据文件的最后修改时间进行排序
  233. */
  234. private class FileLastModifSort implements Comparator<File> {
  235. public int compare(File arg0, File arg1) {
  236. if (arg0.lastModified() > arg1.lastModified()) {
  237. return 1;
  238. } else if (arg0.lastModified() == arg1.lastModified()) {
  239. return 0;
  240. } else {
  241. return -1;
  242. }
  243. }
  244. }
  245.  
  246. /**
  247. * 获取缓存文件的绝对路径
  248. */
  249. private String getCachePath(String key) {
  250. return cacheDir.getAbsolutePath() + File.separator + convertKey(key);
  251. }
  252.  
  253. /**
  254. * 获取磁盘缓存文件夹 优先获取外置磁盘
  255. *
  256. * @param context 上下文
  257. * @param uniqueName 自定义名字
  258. */
  259. public File getDiskCacheDir(Context context, String uniqueName) {
  260. String cachePath;
  261. if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
  262. || !Environment.isExternalStorageRemovable()) {
  263. cachePath = context.getExternalCacheDir().getPath();
  264. } else {
  265. cachePath = context.getCacheDir().getPath();
  266. }
  267. File cacheDir = new File(cachePath + File.separator + uniqueName);
  268. if (!cacheDir.exists())
  269. cacheDir.mkdir();
  270. return cacheDir;
  271. }
  272.  
  273. /**
  274. * 哈希编码
  275. */
  276. public String convertKey(String key) {
  277. String cacheKey;
  278. try {
  279. final MessageDigest mDigest = MessageDigest.getInstance("MD5");
  280. mDigest.update(key.getBytes());
  281. cacheKey = bytesToHexString(mDigest.digest());
  282. } catch (NoSuchAlgorithmException e) {
  283. cacheKey = String.valueOf(key.hashCode());
  284. }
  285. return cacheKey + CACHE_SUFFIX;
  286. }
  287.  
  288. private String bytesToHexString(byte[] bytes) {
  289. StringBuilder sb = new StringBuilder();
  290. for (int i = 0; i < bytes.length; i++) {
  291. String hex = Integer.toHexString(0xFF & bytes[i]);
  292. if (hex.length() == 1) {
  293. sb.append('0');
  294. }
  295. sb.append(hex);
  296. }
  297. return sb.toString();
  298. }
  299.  
  300. }

3.2定义的常量及变量预览。

3.3.构造函数就一个。实现方法在后面。

  

3.4.如何从缓存中获取图片(通过一个String返回一个Bitmap),这个项目暂时没有用到。

  

3.5.如何将图片存入文件缓存?(将一个bitmap,存放一个缓存路径为一个key下)

  

3.6.如何保存bytes数据?(也是存放到一个key中)

  

3.7.如何获取一个本地缓存的输入流?(通过一个key获得FileInputStream)

  

3.8.如何获取本地缓存路径?(通过一个key得到一个String)

  

3.9.如何判断是否有缓存?(通过一个key返回boolean)

  

3.10.然后是关键的整理存储空间了。这个删除文件很关键,不然数据会越来越大的。

  

  

3.11.如何修改文件的最后修改时间?(参数为文件path)

  

3.12.如何计算sdcard上的剩余空间?

  

3.13.如何根据文件的最后修改时间进行排序?(继承了一个Comparator<File>的内部类)

  

  FileLastModifSort其实就是一个类,类中有一个比较的函数,有两个File的参数,比较哪个更加最后而已。

3.14.如何获取缓存文件的绝对路径?(通过key得到一个String)

  

3.15.如何获取磁盘缓存文件夹,优先获取外置磁盘?(参数为context和一个string返回一个File)

  

3.16.什么是哈希编码?(参数为key返回一个String)

  

  通过MD5加密后加了一个后缀为.cache,返回这个字符串。

3.17.bytes数组转化为哈希String?(参数为byte[]返回一个String)

  

  这个在3.16.哈希编码中用到了,就是系统函数很多会转化为bytes,这个函数就是将byte[]类型的数据整合成String,

然后再处理逻辑。

  这个DiskImageCache比较长,主要处理的就是一些缓存问题了,图片缓存,和本地的存储器挂钩了,所以就有很多函数

要调用。

4.回到ImageActivity

4.1这里首先复写了一个getLayoutId,返回一个资源id,其实就是ImageActivity布局

  贴一下activity_image源代码。

  

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--
  3. ~ Copyright 2017 GcsSloop
  4. ~
  5. ~ Licensed under the Apache License, Version 2.0 (the "License");
  6. ~ you may not use this file except in compliance with the License.
  7. ~ You may obtain a copy of the License at
  8. ~
  9. ~ http://www.apache.org/licenses/LICENSE-2.0
  10. ~
  11. ~ Unless required by applicable law or agreed to in writing, software
  12. ~ distributed under the License is distributed on an "AS IS" BASIS,
  13. ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ~ See the License for the specific language governing permissions and
  15. ~ limitations under the License.
  16. ~
  17. ~ Last modified 2017-03-15 23:45:17
  18. ~
  19. ~ GitHub: https://github.com/GcsSloop
  20. ~ Website: http://www.gcssloop.com
  21. ~ Weibo: http://weibo.com/GcsSloop
  22. -->
  23.  
  24. <LinearLayout
  25. xmlns:android="http://schemas.android.com/apk/res/android"
  26. xmlns:tools="http://schemas.android.com/tools"
  27. xmlns:app="http://schemas.android.com/apk/res-auto"
  28. android:id="@+id/activity_image"
  29. android:layout_width="match_parent"
  30. android:layout_height="match_parent"
  31. android:orientation="vertical"
  32. tools:context="com.gcssloop.diycode.activity.ImageActivity">
  33.  
  34. <android.support.v7.widget.Toolbar
  35. android:id="@+id/toolbar"
  36. android:layout_width="match_parent"
  37. android:layout_height="wrap_content"/>
  38.  
  39. <RelativeLayout
  40. android:layout_width="match_parent"
  41. android:layout_height="match_parent">
  42.  
  43. <android.support.v4.view.ViewPager
  44. android:id="@+id/view_pager"
  45. android:layout_width="match_parent"
  46. android:layout_height="match_parent">
  47. </android.support.v4.view.ViewPager>
  48.  
  49. <com.rd.PageIndicatorView
  50. android:id="@+id/pageIndicator"
  51. android:layout_marginBottom="16dp"
  52. android:layout_centerHorizontal="true"
  53. android:layout_alignParentBottom="true"
  54. android:layout_width="wrap_content"
  55. android:layout_height="wrap_content"
  56. app:piv_viewPager="@id/view_pager"
  57. app:piv_animationType="worm"
  58. app:piv_autoVisibility="false"
  59. app:piv_radius="4dp"
  60. app:piv_padding="12dp"
  61. app:piv_unselectedColor="@color/diy_gray2"
  62. app:piv_selectedColor="@color/diy_red"
  63. />
  64.  
  65. </RelativeLayout>
  66.  
  67. </LinearLayout>

4.2分析一下布局代码的结构

  

4.3.和真实页面对比一下。

  

4.4.这里有一个新的知识点==>PageIndicatorView

  可以参考一下这篇文章。

  这是一个开源框架。挺不错的。简单实用。

  作用就是当页面在切换时,一个提示作用,下面的小红点会告诉用户这是第几张图或者第几个页面。

4.5.然后是复写一个initViews方法。

  4.5.1.注意,这个函数在initDatas之后调用。initDatas()函数在BaseImageActivity中调用了。  

  4.5.2.这里面通过setTitle设置了标题栏的查看图片。

  4.5.3.这里从参数ViewHolder中获得了布局中的ViewPager,显然ViewHolder中BaseActivity中定义

     然后ViewHolder初始化其实也是在BaseActivity中进行的,利用了一个抽象函数getLayoutId()

     然后initData(),然后initViews(),所以这个ImageActivity才能直接获得这个ViewPager的Id。

         

  4.5.4.这里看一下initViews中的部分代码。

     

     对于getLayoutInflater不太理解,参考一下这篇文章

     4.5.4.1.它的作用类似于findViewById()。

     4.5.4.2.LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化。

     4.5.4.3.FindViewById是找xml布局文件下的具体widget控件(如Button、TextView等)

  4.5.5.这里继续initViews中的部分代码。

    

    4.5.5.1.这里是在ImageActivity中定义的布局文件中的viewPager设置适配器的过程。

    4.5.5.2.首先是复写了getCount方法,直接返回图片的张数。

    4.5.5.3.然后是复写了isViewFromObject方法,两个参数(View,Object),使两者相等。

    4.5.5.4.然后复写了一个关键的函数,instantiateItem(ViewGroup,int)

       

        对于这个复写函数不明白的,可以参考一下这篇文章。

    4.5.5.6.这里不得不提一下的是PhotoView。

        也是第三方开源库,支持双指缩放,旋转。

        使用方法点击这里

        github原地址点击这里。

        看一下效果图吧!

        

   4.5.5.7.分析一下instantiateItem==>这个方法返回一个对象,这个对象表明了PagerAdapter适配器选

        择那个对象放在当前的ViewPager中。

        然后这里还处理了缓存,如果缓存中有,就利用Glide.with(context).load(file).into()

        如果缓存中没有,就利用Glide.with(context).load(图片url).into()方法       

        然后将这个photoView添加到容器中,这个容器就是instantiateItem中的第一个参数。

   4.5.5.8.分析一下复写的destroyItem()方法,3个参数(ViewGroup,int,Object)

        这个方法,是从ViewGroup中移除当前View。

   4.5.5.9.然后将viewPager确定到第几项。

        调用了基类BaseImageActivity中的静态常量current_image_position。

        这样可以确定指示剂的位置。

 4.6.然后回到ImageActivity中,最后一个函数是loadImage(string,photoView)

      作用:给一个url和一个photoView,加载一张图到photoView中。

      这里用到了一个第三方库。图片加载库。

      Glide用法详解点击这里。

      Glide的原地址点击这里。

      源码如下:

      

      这个应该是设置了缓存策略。

      

      

5.总结一下

5.1.首先理解一下哪里会用到ImageActivity这个活动,这个活动不是局限于某一个模块,而是所有地方,只要包含了图片

  的地方,都是有可能调用intent来跳转到这里的。所以这个有点像之前做的大学喵,点击图片查看大图,支持缩放。

5.2.然后理解一下为什么这个ImageActivity要继承一个BaseImageActivity,这个BaseImageActivity实际上是一个抽象类,

  定义了所有图片数据,所以真实数据在这里面处理,确定当前图片url,当前是第几个图片。

5.3.然后定义了一个DiskImageCache,主要是处理图片缓存的,基本上每个项目都会涉及到图片缓存,而且这些方法都是

  必须实现而且基本一样,所以今后的项目也可以参考一下这个类,封装好的,耦合很低。

5.4.然后是一个imageActivity的布局,通过实现了BaseActivity中的抽象函数getLayoutId,来获得这个布局的资源id。

  然后在initViews中的参数holder中,利用holder.get(R.id.view_pager)就能访问到这个ViewPager。

5.5.初始化view,注意是在initDatas调用之后,因为有了数据,才能填充到真实的视图。这里可以setTilte()来设置标题。

  然后就是一些缓存处理了。

5.6.真正的图片要加载处理,必须要有一个adapter,所以这里new了一个PagerAdapter,实现了4个必须实现的方法。主要

  逻辑在instantiateItem(ViewGroup container,int position)中处理,比如开启photoView缩放功能。以及缓存处理。

5.7.initViews函数执行结束之前一定要设置这个viewPager当前的页面,调用setCurrentItem(int)即可。

  这个int其实是在父类中定义,因为这个是继承了父类,也可以直接调用父类中的变量。

5.8.最后是一个图片缓存策略,因为是灰色的,也没有执行,可以也不是必须要实现的。但是要是不得不说Glide有点强大,

  特别是处理图片问题,图片缓存问题,图片加载问题。有空再深究一下。

Diycode开源项目 ImageActivity分析的更多相关文章

  1. Diycode开源项目 BaseApplication分析+LeakCanary第三方+CrashHandler自定义异常处理

    1.BaseApplication整个应用的开始 1.1.看一下代码 /* * Copyright 2017 GcsSloop * * Licensed under the Apache Licens ...

  2. DiyCode开源项目 BaseActivity 分析

    1.首先将这个项目的BaseActivity源码拷贝过来. /* * Copyright 2017 GcsSloop * * Licensed under the Apache License, Ve ...

  3. Diycode开源项目 TopicContentActivity分析

    1.效果预览以及布局分析 1.1.实际效果预览 左侧话题列表的布局是通过TopicProvider来实现的,所以当初分析话题列表就没有看到布局. 这里的话题内容不是一个ListView,故要自己布局. ...

  4. Diycode开源项目 UserActivity分析

    1.效果预览 1.1.实际界面预览 1.2. 这是MainActivity中的代码 这里执行了跳转到自己的用户界面的功能. 1.3.点击头像或者用户名跳转到别人的页面 UserActivity的结构由 ...

  5. Diycode开源项目 LoginActivity分析

    1.首先看一下效果 1.1.预览一下真实页面 1.2.分析一下: 要求输入Email或者用户名,点击编辑框,弹出键盘,默认先进入输入Email或用户名编辑框. 点击密码后,密码字样网上浮动一段距离,E ...

  6. Diycode开源项目 MainActivity分析

    1.分析MainActivity整体结构 1.1.首先看一下这个界面的整体效果. 1.2.活动源代码如下 /* * Copyright 2017 GcsSloop * * Licensed under ...

  7. DiyCode开源项目 AboutActivity分析

    1.首先看一下效果 这是手机上显示的效果: 1.1首先是一个标题栏,左侧一个左箭头,然后一个图标. 1.2然后下方是一个可以滑动的页面. 1.3分成了7个部分. 1.4DiyCode的图标. 1.5然 ...

  8. DiyCode开源项目 TopicActivity 分析

    1.首先看看TopActivity效果.    2.TopicActivity是一个继承BaseActivity的.前面分析过BaseActivity了.主要有一个标题栏,有返回的图标. 3.贴一下T ...

  9. Diycode开源项目 SitesListFragment分析

    1.效果预览 1.1.网站列表实际界面 1.2.注意这个界面没有继承SimpleRefreshRecycleFragment 前面的话题和新闻继承了SimpleRefreshRecyclerFragm ...

随机推荐

  1. 私有npm下载资源

    私有npm库下载资源需要用户名和密码,这个需要创建npm库的人提供. 使用方法: npm login --registry=仓库地址 Username: 用户名 Password: 密码 Email: ...

  2. 卸载gitlab

    一.停止gitlab sudo gitlab-ctl stop 二.卸载gitlab sudo rpm -e gitlab-ce三.查看gitlab进程 杀掉第一个守护进程 kill -9 4473 ...

  3. 爬虫技术-httpClent+jsoup

    技术:httpClent+jsoup 任务:利用httpClent爬去网站信息,在利用jsoup解析 方法说明: parseUrl(String url):传入相应的url返回该网页内容,网页必须是h ...

  4. jsop解析获得htmldome

    package com.open1111.jsoup; import org.apache.http.HttpEntity;import org.apache.http.client.methods. ...

  5. TP5.0:同一个控制器访问不同方法

    首先,我把TP框架的内容放置在manualtp5文件夹 在manualtp5/application/index/controller/index控制器中定义两个方法: 我们都知道,如果我们网址中不输 ...

  6. 【强力卸载】使用Uninstall Tool卸载各类不易卸载的软件

    Uninstall Tool 经测试卸载MySql5.7.18成功. 下载地址: http://files.cnblogs.com/files/xiaohi/%E3%80%90%E8%BD%AF%E4 ...

  7. Producer & Consumer

    需要与Eureka结合使用 Eureka环境搭建 Producer 一.pom文件 <?xml version="1.0" encoding="UTF-8" ...

  8. 【BZOJ1059】[ZJOI2007] 矩阵游戏(匈牙利算法)

    点此看题面 大致题意: 有一个\(N*N\)的\(01\)矩阵,可以任意交换若干行和若干列,问是否有方案使得左上角到右下角的连线上全是\(1\). 题意转换 首先,让我们来对题意进行一波转化. 如果我 ...

  9. 实现带复选框的TreeView控件

    实现效果: 知识运用: TreeView控件的CheckView属性 //是否在树形视图控件中显示复选框 public bool CheckBoxs{ get;ser } 实现代码: TreeView ...

  10. TypeScript task

    Ctrl+Shift+B 生成 js 文件.