Android imitation WeChat circle of friends Image Viewer

Source: Internet
Author: User

Android imitation circle of friends Image Viewer

 

Before reading this blog post, I hope you can open your posts to the circle of friends and carefully check whether there is a "jiugongge" image area in the circle of friends, clicking an image will jump to the image details page, and the image sliding and scaling are supported? Is this function very common ?! So I just did this Demo today, and I will explain it to you below. First, let's take a look at it according to the Convention, especially not recording GIF animation (Ah ~ No way. The simulator does not support multi-touch. my mobile phone does not have the Root account and cannot screen recording. Sorry, if you want to see the real effect, please move it to the bottom of the blog, click to download the source code, and check the effect again after running the command ~~), Here we will replace a few static images. Sorry!

Homepage ListView effect: Click the jiugongge image to jump to the big picture multi-touch, zoom in the image

Let's take a look! If you really don't understand it, just think about the circle of friends, or drag it below and click to download the source code! Here, let's first analyze the main interface. The layout is very simple. The main interface is just a ListView control. It is worth noting that the Item of ListView contains a GridView, the GridView is used to implement the "jiugongge" effect. The layout of the main interface is a ListView. Let's take a look at the layout of the ListView Item. The following is item_list.xml.

 

 
     
      
       
        
     
    
   
  
 

Well, we can see that the layout is extremely simple, but the problem is that the ListView is nested into the GridView, so a problem will occur, resulting in incomplete display of the GridView, so how can we solve this problem? In fact, it is also simple, that is, rewrite a GridView, measure the height of the GridView, and then set it up. For specific solutions, refer to the previous blogSolution to incomplete display of ListView nested GridViewOr the source code is as follows: NoScrollGridView. java

 

Package com. example. imagedemo; import android. content. context; import android. util. attributeSet; import android. widget. gridView;/*** the custom "jiugongge" -- used to solve the problem in the image set that displays the details of the post: The GridView display is incomplete and only displays a row of images, which is strange, try to override the GridView to solve ** @ author lichao * @ since 2014-10-16 16:41 **/public class NoScrollGridView extends GridView {public NoScrollGridView (Context context) {super (context ); // TODO Auto-generated constructor stub} public NoScrollGridView (Context context, AttributeSet attrs) {super (context, attrs ); // TODO Auto-generated constructor stub} public NoScrollGridView (Context context, AttributeSet attrs, int defStyle) {super (context, attrs, defStyle ); // TODO Auto-generated constructor stub} @ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubint expandSpec = MeasureSpec. makeMeasureSpec (Integer. MAX_VALUE> 2, MeasureSpec. AT_MOST); super. onMeasure (widthMeasureSpec, expandSpec );}}
Next, let's take a look at the data structure of the Item object in the ListView, which is very simple.

 

 

Public class ItemEntity {private String avatar; // user profile URLprivate String title; // title private String content; // content private ArrayList
 
  
ImageUrls; // public ItemEntity (String avatar, String title, String content, ArrayList
  
   
ImageUrls) {super (); this. avatar = avatar; this. title = title; this. content = content; this. imageUrls = imageUrls ;}...}
  
 

 

Well, with ListView, it is inevitable that the data on the Item is adapted. Inherit a BaseAdapter, the Code is as follows, are relatively simple:

 

/*** Homepage ListView data adapter ** @ author Administrator **/public class ListItemAdapter extends BaseAdapter {private Context mContext; private ArrayList
 
  
Items; public ListItemAdapter (Context ctx, ArrayList
  
   
Items) {this. mContext = ctx; this. items = items ;}@ Overridepublic int getCount () {return items = null? 0: items. size () ;}@ Overridepublic Object getItem (int position) {return items. get (position) ;}@ Overridepublic long getItemId (int position) {return position ;}@ Overridepublic View getView (int position, View convertView, ViewGroup parent) {ViewHolder holder; if (convertView = null) {holder = new ViewHolder (); convertView = View. inflate (mContext, R. layout. item_list, null); holder. iv_avatar = (ImageView) convertView. findViewById (R. id. iv_avatar); holder. TV _title = (TextView) convertView. findViewById (R. id. TV _title); holder. TV _content = (TextView) convertView. findViewById (R. id. TV _content); holder. gridview = (NoScrollGridView) convertView. findViewById (R. id. gridview); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag ();} ItemEntity itemEntity = items. get (position); holder. TV _title.setText (itemEntity. getTitle (); holder. TV _content.setText (itemEntity. getContent (); // use ImageLoader to load the network image DisplayImageOptions = new DisplayImageOptions. builder ()//. showImageOnLoading (R. drawable. ic_launcher) // loads the default image. showImageOnFail (R. drawable. ic_launcher) // sets the default image for failed loading. cacheInMemory (true) // memory cache. cacheOnDisk (true) // sdcard cache. bitmapConfig (Config. RGB_565) // set the minimum configuration. build (); // ImageLoader. getInstance (). displayImage (itemEntity. getAvatar (), holder. iv_avatar, options); final ArrayList
   
    
ImageUrls = itemEntity. getImageUrls (); if (imageUrls = null | imageUrls. size () = 0) {// hide GridViewholder without image resources. gridview. setVisibility (View. GONE);} else {holder. gridview. setAdapter (new NoScrollGridAdapter (mContext, imageUrls);} // click to view the larger graph holder. gridview. setOnItemClickListener (new OnItemClickListener () {@ Overridepublic void onItemClick (AdapterView
    Parent, View view, int position, long id) {// TODO Auto-generated method stubimageBrower (position, imageUrls) ;}}); return convertView ;} /*** open the Image Viewer ** @ param position * @ param urls2 */protected void imageBrower (int position, ArrayList
    
     
Urls2) {Intent intent = new Intent (mContext, ImagePagerActivity. class); // image url. To demonstrate the usage of constants, intent is usually obtained from the database or network. putExtra (ImagePagerActivity. EXTRA_IMAGE_URLS, urls2); intent. putExtra (ImagePagerActivity. EXTRA_IMAGE_INDEX, position); mContext. startActivity (intent);}/*** listview Component Reuse to prevent "freezing" ** @ author Administrator **/class ViewHolder {private ImageView iv_avatar; private TextView TV _title; private TextView TV _content; private NoScrollGridView ;}}
    
   
  
 

Here is something that needs to be explained. Let's take a look at the image processing on the listview. Because the images are all obtained from the network, in order to avoid the OOM caused by too many images, therefore, memory optimization is required when images are loaded. There are many image optimization methods. Here I have adopted the simplest and most direct method, using the open-source ImageLoader image loading framework is simply too good, reducing developers' unnecessary and frequent troubles, imageLoader is not the topic to be explained in this blog. For ImageLoader, please download it on the GitHub homepage.

 

Public class MyApplication extends Application {@ Overridepublic void onCreate () {super. onCreate (); DisplayImageOptions defaultOptions = new DisplayImageOptions. builder ()//. showImageForEmptyUri (R. drawable. ic_launcher )//. showImageOnFail (R. drawable. ic_launcher )//. cacheInMemory (true )//. cacheOnDisk (true )//. build (); // ImageLoaderConfiguration config = new ImageLoaderConfiguration //. builder (getApplicationContext ())//. defaultDisplayImageOptions (defaultOptions )//. discCacheSize (50*1024*1024 )//. discCacheFileCount (100) // cache one hundred images. writeDebugLogs ()//. build (); // ImageLoader. getInstance (). init (config );}}
After defining this Application, you need to configure it in the configuration file and add it to the Application node in Manifest. xml:

 

 

android:name=com.example.imagedemo.MyApplication

 

In addition, because ImageLoader is used to obtain images through the network and needs to cache images by local sdcard, you need to add the following permissions:

 

 
  
   
  
 
Let's take a look at the data on the Item above. There is a GridView in it. Obviously, this GridView also requires data adaptation. This data reflects loading images from the network, which is relatively simple. Look at the code NoScrollGridAdapter. java

 

 

      ......@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = View.inflate(ctx, R.layout.item_gridview, null);ImageView imageView = (ImageView) view.findViewById(R.id.iv_image);DisplayImageOptions options = new DisplayImageOptions.Builder()//.cacheInMemory(true)//.cacheOnDisk(true)//.bitmapConfig(Config.RGB_565)//.build();ImageLoader.getInstance().displayImage(imageUrls.get(position),imageView, options);return view;}     ......
In this way, all the data adaptation is done, and then we need to make an image viewer. When we click an image in the "jiugongge" -- NoScrollGridView in the Item on the ListView, you need to pass the url of the image to an image viewer. The image Viewer loads the image over the network based on the url. In fact, the image Viewer is a new independent Activity, this Activity will contain a ViewPager to manage the viewing of multiple images. Image_detail_pager.xml

 

 

 <framelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">    
     
  
 </framelayout>
HackyViewPager. java
Public class HackyViewPager extends ViewPager {private static final String TAG = HackyViewPager; public HackyViewPager (Context context) {super (context);} public HackyViewPager (Context context, AttributeSet attrs) {super (context, attrs) ;}@ Overridepublic boolean onInterceptTouchEvent (MotionEvent ev) {try {return super. onInterceptTouchEvent (ev);} catch (IllegalArgumentException e) {// ignore Log. e (TAG, hacky viewpager error1); return false;} catch (ArrayIndexOutOfBoundsException e) {// ignore Log. e (TAG, hacky viewpager error2); return false ;}}}
ImagePagerActivity. java
/*** Image Viewer */public class ImagePagerActivity extends FragmentActivity {private static final String STATE_POSITION = STATE_POSITION; public static final String EXTRA_IMAGE_INDEX = image_index; public static final String EXTRA_IMAGE_URLS = image_urls; private HackyViewPager mPager; private int pagerPosition; private TextView indicator; @ Overridepublic void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. image_detail_pager); pagerPosition = getIntent (). getIntExtra (EXTRA_IMAGE_INDEX, 0); ArrayList
 
  
Urls = getIntent (). getStringArrayListExtra (EXTRA_IMAGE_URLS); mPager = (HackyViewPager) findViewById (R. id. pager); ImagePagerAdapter mAdapter = new ImagePagerAdapter (getSupportFragmentManager (), urls); mPager. setAdapter (mAdapter); indicator = (TextView) findViewById (R. id. indicator); CharSequence text = getString (R. string. viewpager_indicator, 1, mPager. getAdapter (). getCount (); indicator. setText (text); // more New subscript mPager. setOnPageChangeListener (new OnPageChangeListener () {@ Overridepublic void onPageScrollStateChanged (int arg0) {}@ Overridepublic void onPageScrolled (int arg0, float arg1, int arg2) {}@ Overridepublic void onPageSelected (int arg0) {CharSequence text = getString (R. string. viewpager_indicator, arg0 + 1, mPager. getAdapter (). getCount (); indicator. setText (text) ;}}); if (savedInstanceState! = Null) {pagerPosition = savedInstanceState. getInt (STATE_POSITION);} mPager. setCurrentItem (pagerPosition) ;}@ Overridepublic void onSaveInstanceState (Bundle outState) {outState. putInt (STATE_POSITION, mPager. getCurrentItem ();} private class ImagePagerAdapter extends FragmentStatePagerAdapter {public ArrayList
  
   
FileList; public ImagePagerAdapter (FragmentManager fm, ArrayList
   
    
FileList) {super (fm); this. fileList = fileList ;}@ Overridepublic int getCount () {return fileList = null? 0: fileList. size () ;}@ Overridepublic Fragment getItem (int position) {String url = fileList. get (position); return ImageDetailFragment. newInstance (url );}}}
   
  
 

It is known that the image Viewing Interface is inherited from the FragmentActivity, so the display interface must be implemented by Fragment, so you can customize a Frangment, use this Fragment to obtain image resources from URLs and display images. Image_detail_fragment.xml

 

 <framelayout android:background="@android:color/black" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">    
     
  
 </framelayout>
ImageDetailFragment. java

 

/*** Fragment */public class extends Fragment {private String mImageUrl; private ImageView mImageView; private ProgressBar progressBar; private PhotoViewAttacher mAttacher; public static extends newInstance (String imageUrl) {final ImageDetailFragment f = new ImageDetailFragment (); final Bundle args = new Bundle (); args. putString (url, imageUrl); f. setArguments (args); r Eturn f ;}@ Overridepublic void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); mImageUrl = getArguments ()! = Null? GetArguments (). getString (url): null ;}@ Overridepublic View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {final View v = inflater. inflate (R. layout. image_detail_fragment, container, false); mImageView = (ImageView) v. findViewById (R. id. image); mAttacher = new PhotoViewAttacher (mImageView); mAttacher. setOnPhotoTapListener (new OnPhotoTapListener () {@ Overridepublic void onPhotoTap (View arg0, float arg1, float arg2) {getActivity (). finish () ;}}); progressBar = (ProgressBar) v. findViewById (R. id. loading); return v ;}@ Overridepublic void onActivityCreated (Bundle savedInstanceState) {super. onActivityCreated (savedInstanceState); ImageLoader. getInstance (). displayImage (mImageUrl, mImageView, new SimpleImageLoadingListener () {@ Overridepublic void onLoadingStarted (String imageUri, View view) {progressBar. setVisibility (View. VISIBLE) ;}@ Overridepublic void onLoadingFailed (String imageUri, View view, FailReason failReason) {String message = null; switch (failReason. getType () {case IO_ERROR: message = download error; break; case DECODING_ERROR: message = the image cannot be displayed; break; case NETWORK_DENIED: message = the network has a problem and cannot be downloaded; break; case OUT_OF_MEMORY: message = the image is too large to be displayed; break; case UNKNOWN: message = UNKNOWN error; break;} Toast. makeText (getActivity (), message, Toast. LENGTH_SHORT ). show (); progressBar. setVisibility (View. GONE) ;}@ Overridepublic void onLoadingComplete (String imageUri, View, Bitmap loadedImage) {progressBar. setVisibility (View. GONE); mAttacher. update ();}});}}

This blog post is also completed. It should be noted that the image scaling effect implemented by the image viewer here is the Open Source component PhotoView. The github Project address for PhotoView is here, https://github.com/chrisbanes/PhotoView need to point in the project URL, to download the source code, copy all the source code to the project, the use is quite convenient, demo is as follows:

 

ImageView mImageView;PhotoViewAttacher mAttacher;@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    // Any implementation of ImageView can be used!    mImageView = (ImageView) findViewById(R.id.iv_photo);    // Set the Drawable displayed    Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);    mImageView.setImageDrawable(bitmap);    // Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.    mAttacher = new PhotoViewAttacher(mImageView);}// If you later call mImageView.setImageDrawable/setImageBitmap/setImageResource/etc then you just need to callattacher.update();

 

At the beginning, this image viewer was implemented by my own custom View. In fact, we need to implement Gesture Recognition, multi-touch, and scaling of images, which can be implemented using Matrix, this seems especially troublesome and prone to bugs. This is a bad sign for some "quick success" projects. Therefore, I have abandoned the effect I have customized with Matrix, and switched to the open-source component written for us by github, so that the efficiency will go up, you can also use Matrix to achieve multi-touch scaling. For more information about Matrix, see my previous blog post, Android custom control-3D gallery and image Matrix. In fact, there is no other way to scale images on android. The only thing that can be used is the Matrix class. If you don't believe it, let's look at how the open-source component PhotoView written by Github is implemented, view the following source code:

 

// These are set so we don't keep allocating them on the heap    private final Matrix mBaseMatrix = new Matrix();    private final Matrix mDrawMatrix = new Matrix();    private final Matrix mSuppMatrix = new Matrix();    private final RectF mDisplayRect = new RectF();    private final float[] mMatrixValues = new float[9];
 /**     * Set's the ImageView's ScaleType to Matrix.     */    private static void setImageViewScaleTypeMatrix(ImageView imageView) {        /**         * PhotoView sets it's own ScaleType to Matrix, then diverts all calls         * setScaleType to this.setScaleType automatically.         */        if (null != imageView && !(imageView instanceof IPhotoView)) {            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {                imageView.setScaleType(ScaleType.MATRIX);            }        }    }
The above is only part of the PhotoView source code. We can see at a glance that its implementation is also based on Matrix, and the limitations of time and space. If you need to better understand the implementation of PhotoView, download the source code to view it. To understand the idea of the great god, it requires a solid foundation. I cannot quite understand the specific implementation details of PhotoView, maybe I don't know much about Matrix. I hope to learn more in the future, and I hope to share my studies with you and make progress together!

 

 

Download the source code here

 

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.