Android ultra-high imitation WeChat image selector image loading,

Source: Internet
Author: User

Android ultra-high imitation image selector image loading,
Reprinted please indicate the source: bytes

As for mobile phone image loaders, in today's era where pixels are casually broken by tens of millions, the memory occupied by an image is quite impressive. As a high-performance programmer, it is necessary to master the compression of images, cache and other processing, so that even if you have ten thousand photos, even if your pixels are higher, we can correctly display all the images. Of course, simply displaying images is boring. We decided to copy the image selector. Thank you! This blog is based on the following two blogs:

The Android quick development series creates a omnipotent ListView GridView adapter. We use the CommonAdapter we created as the adapter for the GridView and ListView in our example.

The usage of Android Handler asynchronous message processing mechanism creates powerful image loading classes and uses our own ImageLoader as the core class for image loading.

It doesn't matter if you haven't read it. After reading this blog, you can fully understand the above two articles.

Now, first paste the following:




Dynamic images cannot be recorded. You can open and click to post images, or send images in the chat window, which is similar to the effect ~

Brief description:

1. By default, the folder pictures with the largest number of images are displayed, and the total number of pictures displayed at the bottom is 1;

2. Click the bottom to bring up popupWindow. popupWindow contains all the folders containing images and displays the number of images in each folder. For example, 2. Note: The Activity is dimmed.

3. Select any folder to display the images in the folder. You can click Select image. Of course, clicking the selected image will cancel the selection, for example, 3. Note: The selected image is dimmed.

Of course, the most important effect must be smooth, and it cannot be moved to OOM ~~

I tested the mobile phone Xiaomi for 2 s, and there were 6802 images. There was no OOM exception, and the effect was also very smooth, comparable to the image library ~

However, bugs are inevitable. You can leave a message to discuss the bugs you have found. The source code will be downloaded at the end of this article.

Now, we can start the code journey ~

2. Image list page

First, scan the images on the mobile phone. If you get the most images, they will be directly displayed on the GridView. After the scan is completed, a List of all the folder information containing the images will be obtained;

For the folder information, we create a separate Bean:

Package com. zhy. bean; public class ImageFloder {/*** folder path of the image */private String dir;/*** path of the first image */private String firstImagePath; /*** folder name */private String name;/*** number of images */private int count; public String getDir () {return dir ;} public void setDir (String dir) {this. dir = dir; int lastIndexOf = this. dir. lastIndexOf ("/"); this. name = this. dir. substring (lastIndexOf);} public String getFirstImagePath () {return firstImagePath;} public void setFirstImagePath (String firstImagePath) {this. firstImagePath = firstImagePath;} public String getName () {return name;} public int getCount () {return count;} public void setCount (int count) {this. count = count ;}}

It is used to store the path of the current folder, the number of images contained in the current folder, and the icon used for creating the folder in the first image path. Note: when we set the folder path, automatic Extraction. Take a closer look at the setDir method.

The code for scanning mobile phone images is as follows:

@ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); DisplayMetrics outMetrics = new DisplayMetrics (); getWindowManager (). getdefadisplay display (). getMetrics (outMetrics); mScreenHeight = outMetrics. heightPixels; initView (); getImages (); initEvent ();}/*** uses ContentProvider to scan pictures on your mobile phone. This method scans images in the Child thread, the folder with the most jpg files */private vo Id getImages () {if (! Environment. getExternalStorageState (). equals (Environment. MEDIA_MOUNTED) {Toast. makeText (this, "No external storage", Toast. LENGTH_SHORT ). show (); return;} // display the progress bar mProgressDialog = ProgressDialog. show (this, null, "loading... "); new Thread (new Runnable () {@ Overridepublic void run () {String firstImage = null; Uri mImageUri = MediaStore. images. media. EXTERNAL_CONTENT_URI; ContentResolver mContentResolver = MainActivity. this. g EtContentResolver (); // only query jpeg and png Image Cursor mCursor = mContentResolver. query (mImageUri, null, MediaStore. Images. Media. MIME_TYPE + "=? Or "+ MediaStore. Images. Media. MIME_TYPE +" =? ", New String [] {" image/jpeg "," image/png "}, MediaStore. images. media. DATE_MODIFIED); Log. e ("TAG", mCursor. getCount () + ""); while (mCursor. moveToNext () {// obtain the image path String path = mCursor. getString (mCursor. getColumnIndex (MediaStore. images. media. DATA); Log. e ("TAG", path); // obtain the path of the first image if (firstImage = null) firstImage = path; // obtain the parent path of the image File parentFile = new File (path ). getParentFile (); if (parentFile = n Ull) continue; String dirPath = parentFile. getAbsolutePath (); ImageFloder imageFloder = null; // use a HashSet to prevent multiple scans of the same folder (without this judgment, it is quite scary to add images ~~) If (mDirPaths. contains (dirPath) {continue;} else {mDirPaths. add (dirPath); // initialize imageFloderimageFloder = new ImageFloder (); imageFloder. setDir (dirPath); imageFloder. setFirstImagePath (path);} int picSize = parentFile. list (new FilenameFilter () {@ Overridepublic boolean accept (File dir, String filename) {if (filename. endsWith (". jpg ") | filename. endsWith (". png ") | filename. endsWith (". jpeg ") return true; return false ;}}). length; totalCount + = picSize; imageFloder. setCount (picSize); mImageFloders. add (imageFloder); if (picSize> mPicsSize) {mPicsSize = picSize; mImgDir = parentFile ;}} mCursor. close (); // After scanning is completed, the auxiliary HashSet can also release the memory. mDirPaths = null; // notify Handler to scan the image to complete mHandler. sendEmptyMessage (0x110 );}}). start ();}

InitView won't be viewed. They are all findViewById;

GetImages is mainly used to scan the image code. We enable a Thread for scanning. After scanning, we get the maximum folder path (mImgDir) of images and the number of images in the mobile phone (totalCount ); and all the information including the image folder (mImageFloders)

Then we send a message through handler in handleMessage:

1. Create a GridView adapter and set an adapter for the GridView to display images;

2. With mImageFloders, you can create our popupWindow.

Take a look at our Handler

Private Handler mHandler = new Handler () {public void handleMessage (android. OS. message msg) {mProgressDialog. dismiss (); // bind data2View () to the View; // initialize popupWindwinitListDirPopupWindw () ;}} of the display folder ();}};

We can see that the above two tasks are done respectively:

/*** Bind data to the View */private void data2View () {if (mImgDir = null) {Toast. makeText (getApplicationContext (), "Wipe, an image is not scanned", Toast. LENGTH_SHORT ). show (); return;} mImgs = Arrays. asList (mImgDir. list ();/*** you can see that the path of the folder and the path of the image are saved separately, greatly reducing the memory consumption; */mAdapter = new MyAdapter (getApplicationContext (), mImgs, R. layout. grid_item, mImgDir. getAbsolutePath (); mGirdView. setAdapter (mAdapter); mImageCount. setText (totalCount + "Zhang ");};

Data2View is the set data of all views on the current Activity.

We can see that an Adapter is also used here, and our GridView's:

Package com. zhy. imageloader; import java. util. using list; import java. util. list; import android. content. context; import android. graphics. color; import android. view. view; import android. view. view. onClickListener; import android. widget. imageView; import com. zhy. utils. commonAdapter; public class MyAdapter extends CommonAdapter <String> {/*** user-selected image, full path for storing images */public static List <String> mSelectedImage = new folder List <String> ();/*** folder path */private String mDirPath; public MyAdapter (Context context, List <String> mDatas, int itemLayoutId, String dirPath) {super (context, mDatas, itemLayoutId); this. mDirPath = dirPath;} @ Overridepublic void convert (final com. zhy. utils. viewHolder helper, final String item) {// set no_pichelper.setImageResource (R. id. id_item_image, R. drawable. pictures_no); // set no_selectedhelper.setImageResource (R. id. id_item_select, R. drawable. picture_unselected); // sets the image helper. setImageByUrl (R. id. id_item_image, mDirPath + "/" + item); final ImageView mImageView = helper. getView (R. id. id_item_image); final ImageView mSelect = helper. getView (R. id. id_item_select); mImageView. setColorFilter (null); // sets the ImageView Click Event mImageView. setOnClickListener (new OnClickListener () {// select, the image is dimmed, and vice versa @ Overridepublic void onClick (View v) {// The image if (mSelectedImage. contains (mDirPath + "/" + item) {mSelectedImage. remove (mDirPath + "/" + item); mSelect. setImageResource (R. drawable. picture_unselected); mImageView. setColorFilter (null);} else // This image is not selected {mSelectedImage. add (mDirPath + "/" + item); mSelect. setImageResource (R. drawable. pictures_selected); mImageView. setColorFilter (Color. parseColor ("#77000000") ;}});/*** selected image to show the selected effect */if (mSelectedImage. contains (mDirPath + "/" + item) {mSelect. setImageResource (R. drawable. pictures_selected); mImageView. setColorFilter (Color. parseColor ("#77000000 "));}}}

We can see that our GridView Adapter inherits our CommonAdapter. If you don't know what the CommonAdapter is, you can go to the blog post on the universal Adapter;

Now we only need to implement the convert method:

In convert, we set images, set events, and so on. For image darkening, we use the setColorFilter of ImageView. The operations for loading Images Based on URLs are encapsulated in helper. in setImageByUrl (view, url), our own ImageLoader is used internally, and all the disordered processing is encapsulated. We use LIFO for image policy first-in-first-out; if you are not sure, you can refer to the two blog posts described at the beginning of the article. CommonAdapter and ImageLoader both have a detailed creation process from scratch;

By now, all the tasks of our first Activity have been completed ~~~


3. display the PopupWindow of the folder

Now we want to implement it. Click the layout at the bottom to bring up our folder selection box, and the Activity behind the pop-up box will be dimmed;

Don't rush to post code. Let's first consider how to use PopupWindow best. Our PopupWindow needs to set the layout file, initialize the View, initialize the event, and interact with the Activity ~~

For sure, we use an independent class, which is very similar to Activity, in which initView (), initEvent () and so on.

We created a superclass used by popupWindow:

Package com. zhy. utils; import java. util. list; import android. content. context; import android. graphics. drawable. bitmapDrawable; import android. view. motionEvent; import android. view. view; import android. view. view. onTouchListener; import android. widget. popupWindow; public abstract class BasePopupWindowForListView <T> extends PopupWindow {/*** layout file's outermost View */protected View mContentView; protected Context context ;/*** ListView dataset */protected List <T> mDatas; public BasePopupWindowForListView (View contentView, int width, int height, boolean focusable) {this (contentView, width, height, focusable, null);} public BasePopupWindowForListView (View contentView, int width, int height, boolean focusable, List <T> mDatas) {this (contentView, width, height, focusable, mDatas, new Object [0]);} public BasePopupWindowForListVie W (View contentView, int width, int height, boolean focusable, List <T> mDatas, Object... params) {super (contentView, width, height, focusable); this. mContentView = contentView; context = contentView. getContext (); if (mDatas! = Null) this. mDatas = mDatas; if (params! = Null & params. length> 0) {callback (params);} setBackgroundDrawable (new BitmapDrawable (); setTouchable (true); setOutsideTouchable (true); setTouchInterceptor (new OnTouchListener () {@ Overridepublic boolean onTouch (View v, MotionEvent event) {if (event. getAction () = MotionEvent. ACTION_OUTSIDE) {dismiss (); return true;} return false ;}}); initViews (); initEvents (); init () ;}protected abstract void beforeInitWeNeedSomeParams (Object... params); public abstract void initViews (); public abstract void initEvents (); public abstract void init (); public View findViewById (int id) {return mContentView. findViewById (id);} protected static int dpToPx (Context context, int dp) {return (int) (context. getResources (). getDisplayMetrics (). density * dp + 0.5f );}}

That is, some common settings of popupWindow are encapsulated, And the template-like method mode is used to constrain subclass. methods such as initView, initEvent, and init must be implemented.

Package com. zhy. imageloader; import java. util. list; import android. view. view; import android. widget. adapterView; import android. widget. adapterView. onItemClickListener; import android. widget. listView; import com. zhy. bean. imageFloder; import com. zhy. utils. basePopupWindowForListView; import com. zhy. utils. commonAdapter; import com. zhy. utils. viewHolder; public class ListImageDirPopupWindow extends BasePopupWindow ForListView <ImageFloder> {private ListView mListDir; public ListImageDirPopupWindow (int width, int height, List <ImageFloder> datas, View convertView) {super (convertView, width, height, true, datas) ;}@ Overridepublic void initViews () {mListDir = (ListView) findViewById (R. id. id_list_dir); mListDir. setAdapter (new CommonAdapter <ImageFloder> (context, mDatas, R. layout. list_dir_item) {@ Overridepublic void convert (ViewHolder helper, ImageFloder item) {helper. setText (R. id. id_dir_item_name, item. getName (); helper. setImageByUrl (R. id. id_dir_item_image, item. getFirstImagePath (); helper. setText (R. id. id_dir_item_count, item. getCount () + "Zhang") ;}});} public interface OnImageDirSelected {void selected (ImageFloder floder);} private OnImageDirSelected mImageDirSelected; public void setOnImageDirSelected (OnImageDirSelected mI MageDirSelected) {this. mImageDirSelected = comment;} @ Overridepublic void initEvents () {mListDir. setOnItemClickListener (new OnItemClickListener () {@ Overridepublic void onItemClick (AdapterView <? Parent, View view, int position, long id) {if (mImageDirSelected! = Null) {mImageDirSelected. selected (mDatas. get (position) ;}}}) ;}@ Overridepublic void init () {// TODO Auto-generated method stub }@ Overrideprotected void beforeInitWeNeedSomeParams (Object... params) {// TODO Auto-generated method stub }}
Now we are working on the popupWindow. The layout folder is mainly a ListView, so we have to set its adapter in the initView. Of course, the adapter here still uses our CommonAdapter, several lines of code ~~

Then we need to interact with the Activity. When we click a folder, the outer Activity needs to change its data source in the GridView to display the picture of the folder we clicked;

For interaction, let's look at the pop-up box from the Activity perspective. The Activity wants to know what it wants and just wants to know that another folder has been selected to tell me, so we create an interface OnImageDirSelected and set callback for the Activity;

This can also be written here: publish the ListView of popupWindow and use popupWindow in the Activity. getListView (), setOnItemClickListener. In this case, I personally think it is not good. The coupling degree is too high. The customer simply changed the requirement to "display this folder, change it to GridView, you need to modify the code in the Activity everywhere, because there is a popupWindow in your Activity. getListView.

Okay. The code for initializing the event is as follows:

@Overridepublic void initEvents(){mListDir.setOnItemClickListener(new OnItemClickListener(){@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id){if (mImageDirSelected != null){mImageDirSelected.selected(mDatas.get(position));}}});}

If a callback is set, we call the callback;

At this point, the entire popupWindow is released, and the next step is to show it;

4. Select different folders

As mentioned above, after scanning the image, you can get the list of Folder Information containing the image. This list is the data required by our popupWindow, therefore, the initialization of our popupWindow is in handleMessage (the handler code is pasted on it:

Call initListDirPopupWindw in handleMessage

/*** Initialize popupWindw of the display folder */private void initListDirPopupWindw () {mListImageDirPopupWindow = new ListImageDirPopupWindow (LayoutParams. MATCH_PARENT, (int) (mScreenHeight * 0.7), mImageFloders, LayoutInflater. from (getApplicationContext ()). inflate (R. layout. list_dir, null); mListImageDirPopupWindow. setOnDismissListener (new OnDismissListener () {@ Overridepublic void onDismiss () {// sets the background color to darken WindowManager. layoutParams lp = getWindow (). getAttributes (); lp. alpha = 1.0f; getWindow (). setAttributes (lp) ;}}); // sets the callback mListImageDirPopupWindow for the selected folder. setOnImageDirSelected (this );}
We initialize our popupWindow, set the callback for the close dialog box, and set the callback for selecting different folders;
Here is only initialization. Below we can see that it is appropriate to pop up. In fact, the entire Activity is also an event. Click to bring up this dialog box, so check the initEvents method of the Activity:

Private void initEvent () {/*** sets the click event for the bottom layout. popupWindow */mBottomLy is displayed. setOnClickListener (new OnClickListener () {@ Overridepublic void onClick (View v) {mListImageDirPopupWindow. setAnimationStyle (R. style. anim_popup_dir); mListImageDirPopupWindow. showAsDropDown (mBottomLy, 0, 0); // sets the background color to darken WindowManager. layoutParams lp = getWindow (). getAttributes (); lp. alpha =. 3f; getWindow (). setAttributes (lp );}});}

As you can see, we set click events for the bottom layout; Set pop-up and disappear animations for the popupWindow; the Activity background has been dimmed and highlighted by changing the Window alpha. Highlighted in the listener of the message in the pop-up box ~~

The animation file will not be pasted. You can view the source code by yourself;

PopupWindow is displayed. You can select different folders. Now you should check the selected callback code:

Our Activity implements this interface and looks at the implementation method directly:

@ Overridepublic void selected (ImageFloder floder) {mImgDir = new File (floder. getDir (); mImgs = Arrays. asList (mImgDir. list (new FilenameFilter () {@ Overridepublic boolean accept (File dir, String filename) {if (filename. endsWith (". jpg ") | filename. endsWith (". png ") | filename. endsWith (". jpeg ") return true; return false ;}});/*** you can see that the folder path is saved separately from the image path, greatly reducing memory consumption; */mAdapter = new MyAdapter (getApplicationContext (), mImgs, R. layout. grid_item, mImgDir. getAbsolutePath (); mGirdView. setAdapter (mAdapter); // mAdapter. notifyDataSetChanged (); mImageCount. setText (floder. getCount () + "Zhang"); mChooseDir. setText (floder. getName (); mListImageDirPopupWindow. dismiss ();}

We changed the GridView adapter, the folder name on the control at the bottom, and the number of files;

Well, this is the end. The layout file is not attached to the whole article due to space reasons. You can view it by yourself through the source code;

I hope you can use this case to get the best of it and learn the code style that is worth learning from. Do not study it as an example ~~



Download source code

Ps: Please test it on a real machine. My simulator cannot scan any images ~




Finally, we recommend a Video Teaching site, http://www.imooc.com/seek/detail/id/8, you need to help ask for a class (click to ask for the class can be), the number of students cannot begin, thank you ~

Bytes ---------------------------------------------------------------------------------------------------------

I have created a QQ Group for your convenience. Group Number:55032675






















Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.