2019独角兽企业重金招聘Python工程师标准>>>

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39943731,本文出自:【张鸿洋的博客】

1、概述

关于手机图片加载器,在当今像素随随便便破千万的时代,一张图片占据的内存都相当可观,作为高大尚程序猿的我们,有必要掌握图片的压缩,缓存等处理,以到达纵使你有万张照片,纵使你的像素再高,我们也能正确的显示所有的图片。当然了,单纯显示图片没撒意思,我们决定高仿一下微信的图片选择器,在此,感谢微信!本篇博客将基于以下两篇博客:

Android 快速开发系列 打造万能的ListView GridView 适配器  将使用我们打造的CommonAdapter作为我们例子中GridView以及ListView的适配器

Android Handler 异步消息处理机制的妙用 创建强大的图片加载类 将使用我们自己写的ImageLoader作为我们的图片加载的核心类

如果你没看过也没关系,等看完本篇博客,可以结合以上两篇再进行充分理解一下。

好了,首先贴一下效果图:

动态图实在是录不出来,大家自己打开微信点击发表图片,或者聊天窗口发送图片,大致和微信的效果一样~

简单描述一下:

1、默认显示图片最多的文件夹图片,以及底部显示图片总数量;如上图1;

2、点击底部,弹出popupWindow,popupWindow包含所有含有图片的文件夹,以及显示每个文件夹中图片数量;如上图2;注:此时Activity变暗

3、选择任何文件夹,进入该文件夹图片显示,可以点击选择图片,当然了,点击已选择的图片则会取消选择;如上图3;注:选中图片变暗

当然了,最重要的效果一定流畅,不能动不动OOM~~

本人测试手机小米2s,图片6802张,未出现OOM异常,效果也是非常流畅,堪比图库~

不过存在bug在所难免,大家可以留言说下自己发现的bug;文末会提供源码下载。

好了,下面就可以代码的征程了~

2、图片的列表页

首先对手机中图片进行扫描,拿到图片数量最多的,直接显示在GridView上;并且扫描结束,得到一个所有包含图片的文件夹信息的List;

对于文件夹信息,我们单独创建了一个Bean:

[java] view plaincopy

  1. package com.zhy.bean;

  2. public class ImageFloder

  3. {

  4. /**

  5. * 图片的文件夹路径

  6. */

  7. private String dir;

  8. /**

  9. * 第一张图片的路径

  10. */

  11. private String firstImagePath;

  12. /**

  13. * 文件夹的名称

  14. */

  15. private String name;

  16. /**

  17. * 图片的数量

  18. */

  19. private int count;

  20. public String getDir()

  21. {

  22. return dir;

  23. }

  24. public void setDir(String dir)

  25. {

  26. this.dir = dir;

  27. int lastIndexOf = this.dir.lastIndexOf("/");

  28. this.name = this.dir.substring(lastIndexOf);

  29. }

  30. public String getFirstImagePath()

  31. {

  32. return firstImagePath;

  33. }

  34. public void setFirstImagePath(String firstImagePath)

  35. {

  36. this.firstImagePath = firstImagePath;

  37. }

  38. public String getName()

  39. {

  40. return name;

  41. }

  42. public int getCount()

  43. {

  44. return count;

  45. }

  46. public void setCount(int count)

  47. {

  48. this.count = count;

  49. }

  50. }

用来存储当前文件夹的路径,当前文件夹包含多少张图片,以及第一张图片路径用于做文件夹的图标;注:文件夹的名称,我们在set文件夹的路径的时候,自动提取,仔细看下setDir这个方法。

接下来就是扫描手机图片的代码了:

[java] view plaincopy

  1. @Override

  2. protected void onCreate(Bundle savedInstanceState)

  3. {

  4. super.onCreate(savedInstanceState);

  5. setContentView(R.layout.activity_main);

  6. DisplayMetrics outMetrics = new DisplayMetrics();

  7. getWindowManager().getDefaultDisplay().getMetrics(outMetrics);

  8. mScreenHeight = outMetrics.heightPixels;

  9. initView();

  10. getImages();

  11. initEvent();

  12. }

  13. /**

  14. * 利用ContentProvider扫描手机中的图片,此方法在运行在子线程中 完成图片的扫描,最终获得jpg最多的那个文件夹

  15. */

  16. private void getImages()

  17. {

  18. if (!Environment.getExternalStorageState().equals(

  19. Environment.MEDIA_MOUNTED))

  20. {

  21. Toast.makeText(this, "暂无外部存储", Toast.LENGTH_SHORT).show();

  22. return;

  23. }

  24. // 显示进度条

  25. mProgressDialog = ProgressDialog.show(this, null, "正在加载...");

  26. new Thread(new Runnable()

  27. {

  28. @Override

  29. public void run()

  30. {

  31. String firstImage = null;

  32. Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

  33. ContentResolver mContentResolver = MainActivity.this

  34. .getContentResolver();

  35. // 只查询jpeg和png的图片

  36. Cursor mCursor = mContentResolver.query(mImageUri, null,

  37. MediaStore.Images.Media.MIME_TYPE + "=? or "

  38. + MediaStore.Images.Media.MIME_TYPE + "=?",

  39. new String[] { "image/jpeg", "image/png" },

  40. MediaStore.Images.Media.DATE_MODIFIED);

  41. Log.e("TAG", mCursor.getCount() + "");

  42. while (mCursor.moveToNext())

  43. {

  44. // 获取图片的路径

  45. String path = mCursor.getString(mCursor

  46. .getColumnIndex(MediaStore.Images.Media.DATA));

  47. Log.e("TAG", path);

  48. // 拿到第一张图片的路径

  49. if (firstImage == null)

  50. firstImage = path;

  51. // 获取该图片的父路径名

  52. File parentFile = new File(path).getParentFile();

  53. if (parentFile == null)

  54. continue;

  55. String dirPath = parentFile.getAbsolutePath();

  56. ImageFloder imageFloder = null;

  57. // 利用一个HashSet防止多次扫描同一个文件夹(不加这个判断,图片多起来还是相当恐怖的~~)

  58. if (mDirPaths.contains(dirPath))

  59. {

  60. continue;

  61. } else

  62. {

  63. mDirPaths.add(dirPath);

  64. // 初始化imageFloder

  65. imageFloder = new ImageFloder();

  66. imageFloder.setDir(dirPath);

  67. imageFloder.setFirstImagePath(path);

  68. }

  69. int picSize = parentFile.list(new FilenameFilter()

  70. {

  71. @Override

  72. public boolean accept(File dir, String filename)

  73. {

  74. if (filename.endsWith(".jpg")

  75. || filename.endsWith(".png")

  76. || filename.endsWith(".jpeg"))

  77. return true;

  78. return false;

  79. }

  80. }).length;

  81. totalCount += picSize;

  82. imageFloder.setCount(picSize);

  83. mImageFloders.add(imageFloder);

  84. if (picSize > mPicsSize)

  85. {

  86. mPicsSize = picSize;

  87. mImgDir = parentFile;

  88. }

  89. }

  90. mCursor.close();

  91. // 扫描完成,辅助的HashSet也就可以释放内存了

  92. mDirPaths = null;

  93. // 通知Handler扫描图片完成

  94. mHandler.sendEmptyMessage(0x110);

  95. }

  96. }).start();

  97. }

ps:运行出现空指针的话,在81行的位置添加判断,if(parentFile.list()==null)continue , 切记~~~有些图片比较诡异~~;

initView就不看了,都是些findViewById;

getImages主要就是扫描图片的代码,我们开启了一个Thread进行扫描,扫描完成以后,我们得到了图片最多文件夹路径(mImgDir),手机中图片数量(totalCount);以及所有包含图片文件夹信息(mImageFloders)

然后我们通过handler发送消息,在handleMessage里面:

1、创建GridView的适配器,为我们的GridView设置适配器,显示图片;

2、有了mImageFloders,就可以创建我们的popupWindow了

看一眼我们的Handler

[java] view plaincopy

  1. private Handler mHandler = new Handler()

  2. {

  3. public void handleMessage(android.os.Message msg)

  4. {

  5. mProgressDialog.dismiss();

  6. //为View绑定数据

  7. data2View();

  8. //初始化展示文件夹的popupWindw

  9. initListDirPopupWindw();

  10. }

  11. };

可以看到分别干了上述的两件事:

[java] view plaincopy

  1. /**

  2. * 为View绑定数据

  3. */

  4. private void data2View()

  5. {

  6. if (mImgDir == null)

  7. {

  8. Toast.makeText(getApplicationContext(), "擦,一张图片没扫描到",

  9. Toast.LENGTH_SHORT).show();

  10. return;

  11. }

  12. mImgs = Arrays.asList(mImgDir.list());

  13. /**

  14. * 可以看到文件夹的路径和图片的路径分开保存,极大的减少了内存的消耗;

  15. */

  16. mAdapter = new MyAdapter(getApplicationContext(), mImgs,

  17. R.layout.grid_item, mImgDir.getAbsolutePath());

  18. mGirdView.setAdapter(mAdapter);

  19. mImageCount.setText(totalCount + "张");

  20. };

data2View就是我们当前Activity上所有的View设置数据了。

看到这里还用到了一个Adapter,我们GridView的:

[java] view plaincopy

  1. package com.zhy.imageloader;

  2. import java.util.LinkedList;

  3. import java.util.List;

  4. import android.content.Context;

  5. import android.graphics.Color;

  6. import android.view.View;

  7. import android.view.View.OnClickListener;

  8. import android.widget.ImageView;

  9. import com.zhy.utils.CommonAdapter;

  10. public class MyAdapter extends CommonAdapter<String>

  11. {

  12. /**

  13. * 用户选择的图片,存储为图片的完整路径

  14. */

  15. public static List<String> mSelectedImage = new LinkedList<String>();

  16. /**

  17. * 文件夹路径

  18. */

  19. private String mDirPath;

  20. public MyAdapter(Context context, List<String> mDatas, int itemLayoutId,

  21. String dirPath)

  22. {

  23. super(context, mDatas, itemLayoutId);

  24. this.mDirPath = dirPath;

  25. }

  26. @Override

  27. public void convert(final com.zhy.utils.ViewHolder helper, final String item)

  28. {

  29. // 设置no_pic

  30. helper.setImageResource(R.id.id_item_image, R.drawable.pictures_no);

  31. // 设置no_selected

  32. helper.setImageResource(R.id.id_item_select,

  33. R.drawable.picture_unselected);

  34. // 设置图片

  35. helper.setImageByUrl(R.id.id_item_image, mDirPath + "/" + item);

  36. final ImageView mImageView = helper.getView(R.id.id_item_image);

  37. final ImageView mSelect = helper.getView(R.id.id_item_select);

  38. mImageView.setColorFilter(null);

  39. // 设置ImageView的点击事件

  40. mImageView.setOnClickListener(new OnClickListener()

  41. {

  42. // 选择,则将图片变暗,反之则反之

  43. @Override

  44. public void onClick(View v)

  45. {

  46. // 已经选择过该图片

  47. if (mSelectedImage.contains(mDirPath + "/" + item))

  48. {

  49. mSelectedImage.remove(mDirPath + "/" + item);

  50. mSelect.setImageResource(R.drawable.picture_unselected);

  51. mImageView.setColorFilter(null);

  52. } else

  53. // 未选择该图片

  54. {

  55. mSelectedImage.add(mDirPath + "/" + item);

  56. mSelect.setImageResource(R.drawable.pictures_selected);

  57. mImageView.setColorFilter(Color.parseColor("#77000000"));

  58. }

  59. }

  60. });

  61. /**

  62. * 已经选择过的图片,显示出选择过的效果

  63. */

  64. if (mSelectedImage.contains(mDirPath + "/" + item))

  65. {

  66. mSelect.setImageResource(R.drawable.pictures_selected);

  67. mImageView.setColorFilter(Color.parseColor("#77000000"));

  68. }

  69. }

  70. }

可以看到我们GridView的Adapter继承了我们的CommonAdapter,如果不知道CommonAdapter为何物,可以去看看万能适配器那篇博文;

我们现在只需要实现convert方法:

在convert中,我们设置图片,设置事件等,对于图片的变暗,我们使用的是ImageView的setColorFilter ;根据Url加载图片的操作封装在helper.setImageByUrl(view,url)中,内部使用的是我们自己定义的ImageLoader,包括错乱处理都已经封装了,图片策略我们使用的是LIFO后进先出;不清楚的可以看文章一开始说明的那两篇博文,对于CommonAdapter以及ImageLoader都有从无到有的详细打造过程;

到此我们的第一个Activity的所有的任务就完成了~~~

3、展现文件夹的PopupWindow

现在我们要实现,点击底部的布局弹出我们的文件夹选择框,并且我们弹出框后面的Activity要变暗;

不急着贴代码,我们先考虑下PopupWindow怎么用最好,我们的PopupWindow需要设置布局文件,需要初始化View,需要初始化事件,还需要和Activity交互~~

那么肯定的,我们使用独立的类,这个类和Activity很相似,在里面initView(),initEvent()之类的。

我们创建了一个popupWindow使用的超类:

[java] view plaincopy

  1. package com.zhy.utils;

  2. import java.util.List;

  3. import android.content.Context;

  4. import android.graphics.drawable.BitmapDrawable;

  5. import android.view.MotionEvent;

  6. import android.view.View;

  7. import android.view.View.OnTouchListener;

  8. import android.widget.PopupWindow;

  9. public abstract class BasePopupWindowForListView<T> extends PopupWindow

  10. {

  11. /**

  12. * 布局文件的最外层View

  13. */

  14. protected View mContentView;

  15. protected Context context;

  16. /**

  17. * ListView的数据集

  18. */

  19. protected List<T> mDatas;

  20. public BasePopupWindowForListView(View contentView, int width, int height,

  21. boolean focusable)

  22. {

  23. this(contentView, width, height, focusable, null);

  24. }

  25. public BasePopupWindowForListView(View contentView, int width, int height,

  26. boolean focusable, List<T> mDatas)

  27. {

  28. this(contentView, width, height, focusable, mDatas, new Object[0]);

  29. }

  30. public BasePopupWindowForListView(View contentView, int width, int height,

  31. boolean focusable, List<T> mDatas, Object... params)

  32. {

  33. super(contentView, width, height, focusable);

  34. this.mContentView = contentView;

  35. context = contentView.getContext();

  36. if (mDatas != null)

  37. this.mDatas = mDatas;

  38. if (params != null && params.length > 0)

  39. {

  40. beforeInitWeNeedSomeParams(params);

  41. }

  42. setBackgroundDrawable(new BitmapDrawable());

  43. setTouchable(true);

  44. setOutsideTouchable(true);

  45. setTouchInterceptor(new OnTouchListener()

  46. {

  47. @Override

  48. public boolean onTouch(View v, MotionEvent event)

  49. {

  50. if (event.getAction() == MotionEvent.ACTION_OUTSIDE)

  51. {

  52. dismiss();

  53. return true;

  54. }

  55. return false;

  56. }

  57. });

  58. initViews();

  59. initEvents();

  60. init();

  61. }

  62. protected abstract void beforeInitWeNeedSomeParams(Object... params);

  63. public abstract void initViews();

  64. public abstract void initEvents();

  65. public abstract void init();

  66. public View findViewById(int id)

  67. {

  68. return mContentView.findViewById(id);

  69. }

  70. protected static int dpToPx(Context context, int dp)

  71. {

  72. return (int) (context.getResources().getDisplayMetrics().density * dp + 0.5f);

  73. }

  74. }

也就是封装了一下popupWindow常用的一些设置,然后使用了类似模版方法模式,约束子类,必须实现initView,initEvent,init等方法

[java] view plaincopy

  1. package com.zhy.imageloader;

  2. import java.util.List;

  3. import android.view.View;

  4. import android.widget.AdapterView;

  5. import android.widget.AdapterView.OnItemClickListener;

  6. import android.widget.ListView;

  7. import com.zhy.bean.ImageFloder;

  8. import com.zhy.utils.BasePopupWindowForListView;

  9. import com.zhy.utils.CommonAdapter;

  10. import com.zhy.utils.ViewHolder;

  11. public class ListImageDirPopupWindow extends BasePopupWindowForListView<ImageFloder>

  12. {

  13. private ListView mListDir;

  14. public ListImageDirPopupWindow(int width, int height,

  15. List<ImageFloder> datas, View convertView)

  16. {

  17. super(convertView, width, height, true, datas);

  18. }

  19. @Override

  20. public void initViews()

  21. {

  22. mListDir = (ListView) findViewById(R.id.id_list_dir);

  23. mListDir.setAdapter(new CommonAdapter<ImageFloder>(context, mDatas,

  24. R.layout.list_dir_item)

  25. {

  26. @Override

  27. public void convert(ViewHolder helper, ImageFloder item)

  28. {

  29. helper.setText(R.id.id_dir_item_name, item.getName());

  30. helper.setImageByUrl(R.id.id_dir_item_image,

  31. item.getFirstImagePath());

  32. helper.setText(R.id.id_dir_item_count, item.getCount() + "张");

  33. }

  34. });

  35. }

  36. public interface OnImageDirSelected

  37. {

  38. void selected(ImageFloder floder);

  39. }

  40. private OnImageDirSelected mImageDirSelected;

  41. public void setOnImageDirSelected(OnImageDirSelected mImageDirSelected)

  42. {

  43. this.mImageDirSelected = mImageDirSelected;

  44. }

  45. @Override

  46. public void initEvents()

  47. {

  48. mListDir.setOnItemClickListener(new OnItemClickListener()

  49. {

  50. @Override

  51. public void onItemClick(AdapterView<?> parent, View view,

  52. int position, long id)

  53. {

  54. if (mImageDirSelected != null)

  55. {

  56. mImageDirSelected.selected(mDatas.get(position));

  57. }

  58. }

  59. });

  60. }

  61. @Override

  62. public void init()

  63. {

  64. // TODO Auto-generated method stub

  65. }

  66. @Override

  67. protected void beforeInitWeNeedSomeParams(Object... params)

  68. {

  69. // TODO Auto-generated method stub

  70. }

  71. }

好了,现在就是我们正在的popupWindow咯,布局文件夹主要是个ListView,所以在initView里面,我们得设置它的适配器;当然了,这里的适配器依然用我们的CommonAdapter,几行代码搞定~~

然后我们需要和Activity交互,当我们点击某个文件夹的时候,外层的Activity需要改变它GridView的数据源,展示我们点击文件夹的图片;

关于交互,我们从Activity的角度去看弹出框,Activity想知道什么,只想知道选择了别的文件夹来告诉我,所以我们创建一个接口OnImageDirSelected,对Activity设置回调;

这里还可以这么写:就是把popupWindow的ListView公布出去,然后在Activity里面使用popupWindow.getListView(),setOnItemClickListener,这么做,个人觉得不好,耦合度太高,客户简单改下需求“这个文件夹展示,给我们换了,换成GridView”,呵呵,此时,你需要到处去修改Activity里面的代码,因为你Activity里面竟然还有个popupWindow.getListView。

好了,扯多了,初始化事件的代码:

[java] view plaincopy

  1. @Override

  2. public void initEvents()

  3. {

  4. mListDir.setOnItemClickListener(new OnItemClickListener()

  5. {

  6. @Override

  7. public void onItemClick(AdapterView<?> parent, View view,

  8. int position, long id)

  9. {

  10. if (mImageDirSelected != null)

  11. {

  12. mImageDirSelected.selected(mDatas.get(position));

  13. }

  14. }

  15. });

  16. }

如果有人设置了回调,我们就调用;

到此,整个popupWindow就出炉了,接下来就看啥时候让它展示了;

4、选择不同的文件夹

上面说道,当扫描图片完成,拿到包含图片的文件夹信息列表;这个列表就是我们popupWindow所需的数据,所以我们的popupWindow的初始化在handleMessage(上面贴了handler的代码)里面:

在handleMessage里面调用initListDirPopupWindw

[java] view plaincopy

  1. /**

  2. * 初始化展示文件夹的popupWindw

  3. */

  4. private void initListDirPopupWindw()

  5. {

  6. mListImageDirPopupWindow = new ListImageDirPopupWindow(

  7. LayoutParams.MATCH_PARENT, (int) (mScreenHeight * 0.7),

  8. mImageFloders, LayoutInflater.from(getApplicationContext())

  9. .inflate(R.layout.list_dir, null));

  10. mListImageDirPopupWindow.setOnDismissListener(new OnDismissListener()

  11. {

  12. @Override

  13. public void onDismiss()

  14. {

  15. // 设置背景颜色变暗

  16. WindowManager.LayoutParams lp = getWindow().getAttributes();

  17. lp.alpha = 1.0f;

  18. getWindow().setAttributes(lp);

  19. }

  20. });

  21. // 设置选择文件夹的回调

  22. mListImageDirPopupWindow.setOnImageDirSelected(this);

  23. }

我们初始化我们的popupWindow,设置了关闭对话框的回调,已经设置了选择不同文件夹的回调;
这里仅仅是初始化,下面看我们合适将其弹出的,其实整个Activity也就一个事件,点击弹出该对话框,所以看Activity的initEvents方法:

[java] view plaincopy

  1. private void initEvent()

  2. {

  3. /**

  4. * 为底部的布局设置点击事件,弹出popupWindow

  5. */

  6. mBottomLy.setOnClickListener(new OnClickListener()

  7. {

  8. @Override

  9. public void onClick(View v)

  10. {

  11. mListImageDirPopupWindow

  12. .setAnimationStyle(R.style.anim_popup_dir);

  13. mListImageDirPopupWindow.showAsDropDown(mBottomLy, 0, 0);

  14. // 设置背景颜色变暗

  15. WindowManager.LayoutParams lp = getWindow().getAttributes();

  16. lp.alpha = .3f;

  17. getWindow().setAttributes(lp);

  18. }

  19. });

  20. }

可以看到,我们为底部布局设置点击事件;设置popupWindow的弹出与消失的动画;已经让Activity背景变暗变亮,通过改变Window alpha实现的。变亮在弹出框消息的监听里面~~

动画的文件就不贴了,大家自己看源码;

popupWindow弹出了,用户此时可以选择不同的文件夹,那么现在该看选择后的回调的代码了:

我们的Activity实现了该接口,直接看实现的方法:

[java] view plaincopy

  1. @Override

  2. public void selected(ImageFloder floder)

  3. {

  4. mImgDir = new File(floder.getDir());

  5. mImgs = Arrays.asList(mImgDir.list(new FilenameFilter()

  6. {

  7. @Override

  8. public boolean accept(File dir, String filename)

  9. {

  10. if (filename.endsWith(".jpg") || filename.endsWith(".png")

  11. || filename.endsWith(".jpeg"))

  12. return true;

  13. return false;

  14. }

  15. }));

  16. /**

  17. * 可以看到文件夹的路径和图片的路径分开保存,极大的减少了内存的消耗;

  18. */

  19. mAdapter = new MyAdapter(getApplicationContext(), mImgs,

  20. R.layout.grid_item, mImgDir.getAbsolutePath());

  21. mGirdView.setAdapter(mAdapter);

  22. // mAdapter.notifyDataSetChanged();

  23. mImageCount.setText(floder.getCount() + "张");

  24. mChooseDir.setText(floder.getName());

  25. mListImageDirPopupWindow.dismiss();

  26. }

我们改变了GridView的适配器,以及底部的控件上的文件夹名称,文件数量等等;

好了,到此结束;整篇由于篇幅原因没有贴任何布局文件,大家自己通过源码查看;

在此希望大家可以通过该案例,能够去其糟粕,取其精华,学习其中值得借鉴的代码风格,不要真的当作一个例子去学习~~

源码点击下载

ps:请真机测试,反正我的模拟器扫描不到图片~

ps:运行出现空指针的话,在getImages中添加判断,if(parentFile.list()==null)continue , 切记~~~具体位置,上面有说;

---------------------------------------------------------------------------------------------------------

我建了一个QQ群,方便大家交流。群号:55032675

----------------------------------------------------------------------------------------------------------

博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0侧滑

转载于:https://my.oschina.net/u/1177694/blog/510120

Android 超高仿微信图片选择器 图片该这么加载相关推荐

  1. Android 超高仿微信图片选择器

    出处: http://blog.csdn.net/lmj623565791/article/details/39943731 1.概述 关于手机图片加载器,在当今像素随随便便破千万的时代,一张图片占据 ...

  2. Android 超高仿微信图片选择器 图片该这么加载

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/39943731,本文出自: [张鸿洋的博客] 1.概述 关于手机图片加载器,在当 ...

  3. Android 超高仿微信图片选择器 图片该这么载入

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/39943731,本文出自: [张鸿洋的博客] 1.概述 关于手机图片载入器,在当 ...

  4. Android高仿微信照片选择器+预览+显示照片

    转载请说明出处: http://blog.csdn.net/lyhhj/article/details/49046109 前阵子写过一片博客,是关于选择多图上传的一个小demo,那个demo是从网上找 ...

  5. android仿微信图片上传进度,android高仿微信发布动态(选择图片)

    [实例简介]Android 超高仿微信图片选择器 [实例截图] [核心代码] public class MainActivity extends Activity implements OnImage ...

  6. android 仿快递步骤_Android实现仿美团、顺丰快递数据加载效果

    我们都知道在Android中,常见的动画模式有两种:一种是帧动画(Frame Animation),一种是补间动画(Tween Animation).帧动画是提供了一种逐帧播放图片的动画方式,播放事先 ...

  7. Android 实现仿微信朋友圈九宫格图片+NineGridView+ImageWatcher(图片查看:1.预览,2.拖动,3.放大,4.左右滑动,5.长按保存到手机)的功能

    一.测试 实现: 二.添加依赖包: implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.recycl ...

  8. android高仿微信UI点击头像显示大图片效果

    用过微信的朋友朋友都见过微信中点击对方头像显示会加载大图,先贴两张图片说明下: 这种UI效果对用户的体验不错,今天突然有了灵感,试着去实现,结果就出来了.. 下面说说我的思路: 1.点击图片时跳转到另 ...

  9. android 仿微信 视频播放器,Android仿微信多媒体选择器 - SmartMediaPicker

    2019.5.27 更新 版本已更新至[1.1.1]详情参考GitHub. 多媒体选择器 SmartMediaPicker 好久没写简书了,这次带来的是自己封装的一个多媒体选择器.这是一款方便好用的仿 ...

最新文章

  1. 祝福互动html页面,祝福.html
  2. R语言使用magick包的image_write函数将已有图像以任何指定的格式导出保存到磁盘上(例如将原文将从png转化为jpeg)
  3. 《Implementing QuantLib》译后记
  4. [总结篇4] l2-agent的细节
  5. java文件名大小_java 比较指定文件夹内.txt文件名的大小
  6. 【网易云信】即时通讯SDK DEMO
  7. 响应面matlab算法,结构可靠度分析的响应面法及其Matlab实现参考文件.pdf
  8. python毕业设计作品基于django框架 二手物品交易系统毕设成品(6)开题答辩PPT
  9. 一键配置以太网IP脚本
  10. 深度学习的可解释性!
  11. IP地址 A\B\C类
  12. 机器学习算法基础20210504-0529
  13. 递归最小二乘法、增广最小二乘法、带遗忘因子的递归增广最小二乘法
  14. caj怎么转pdf?
  15. Arduino从零开始学习(一)
  16. 3d图形消隐c语言,一种3D图形背面消隐算法及其硬件加速实现
  17. 用inno Setup做应用程序安装包的示例脚本(.iss文件)(
  18. 如何使用charles+mock替换接口返回来测试
  19. 国密局公开SM2和SM3算法或预示中国商密算法将走向开放
  20. 物业管理信息系统源码

热门文章

  1. word2vec模型的理解
  2. 数字图像处理技术详解程序_安装地暖施工程序有哪些 安装地暖技术要求是什么【详解】...
  3. 电脑不能打字_宝妈、学生、上班族手机兼职——打字录入
  4. linux 有用的命令,linux有用命令(持续更新)
  5. 二叉树(BST)之创建二叉搜索树
  6. LeetCode:64. 最小路径和
  7. 信息系统项目管理师优秀论文:项目进度管理
  8. 信息系统项目管理师论文范文-质量管理
  9. 笔记-项目配置管理-配置项
  10. 笔记-项目整体管理-变更管理-实施整体变更控制