The picture selector in android realizes the function of selecting and uploading multiple pictures of wechat publishing circle of friends

Posted by Ralf Jones on Sat, 25 Dec 2021 16:21:10 +0100

This article is based on the ideas required by the project to learn from the improved ideas of other bloggers. The ideas will be the same, but this article does belong to my original creation, and the reference link will be displayed at the end of the article (ah, good official...)

Let's talk about the functional requirements of the project, which is similar to the interface of QQ and wechat when reporting / complaining users. Users need to upload chat screenshots of relevant violations. Of course, users have saved screenshots in mobile phone photo albums before realizing this function, so the function of multi image upload is relatively simple, At that time, I also found a lot of information from the Internet when doing this function. At the beginning, my idea was to adjust the system album with code, and then select pictures from the system album, but after running, I found that only one picture can be selected, not more than one, This is not in line with the development logic (the main reason is that I don't know there is a picture selector framework due to my lack of experience). I found that there are many kinds of frames related to picture selectors, including native ones and ones directly typed by Daniel with code hands, (but I prefer the original ones, because Daniel's ideas are not understood by everyone...) so I won't talk about Daniel's more. Let me give some examples of the original ones, which are easy to understand by junior coders. Oh, no, let's introduce them to you directly!

Layout code: only show the part of the code that displays the picture

<androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
           
            <GridView
                android:id="@+id/add_pic"
                android:layout_width="200dp"
                android:layout_height="fill_parent"
                android:layout_marginStart="30dp"
                android:background="@drawable/input_bg"
                android:minHeight="56dp"
                android:scrollbars="none"
                android:layout_margin="10dp"
                android:visibility="gone"
                android:gravity="fill_horizontal"
                android:stretchMode="columnWidth"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toLeftOf="@id/btn_add_pic"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
//Click this button to jump to the system picture selector
            <Button
                android:id="@+id/btn_add_pic"
                android:layout_width="56dp"
                android:layout_height="56dp"
                android:layout_margin="10dp"
                android:background="@drawable/add_picture_bg"
                android:foreground="@mipmap/add"
                android:foregroundGravity="center"
                app:backgroundTint="#EFEFEF"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toEndOf="@+id/add_pic"
                app:layout_constraintTop_toTopOf="parent" />
                
        </androidx.constraintlayout.widget.ConstraintLayout>

The layout made according to the project requirements is in this style:

Next is the function implementation of java code:

public   class btnAddPicOnClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {
       		 //Turn on the multi graph selector
            Intent intent=new Intent(MainActivity.this, ImagesSelectorActivity.class);
            //Sets the maximum number of selected pictures
            intent.putExtra(SelectorSettings.SELECTOR_MAX_IMAGE_NUMBER,3);
            //Setting the minimum size of the displayed image; Filter small images (mainly icons)
            intent.putExtra(SelectorSettings.SELECTOR_MIN_IMAGE_SIZE,100000);
            //Show camera
            intent.putExtra(SelectorSettings.SELECTOR_SHOW_CAMERA,false);
            //Takes the currently selected image set as the initial value
            intent.putStringArrayListExtra(SelectorSettings.SELECTOR_INITIAL_SELECTED_LIST,imagePaths);
            //Jump to multi graph selector
            startActivityForResult(intent,REQUEST_CODE);
        }
    }
//Get the image path response startActivityForResult and display the image in the grid layout
    @SuppressLint("LongLogTag")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //requestCode: status identification code
        if (requestCode==REQUEST_CODE){
        //resultCode: result identification code
            if (resultCode==RESULT_OK){
            //Get selection results from selector
               ArrayList<String> list=data.getStringArrayListExtra(SelectorSettings.SELECTOR_RESULTS);
               //Displays the selected picture
               loadAdapter(list);
                Log.i("jjjj", "list: "+ list.toString());
                //The grid layout is hidden at the beginning, mainly to make the displayed layout look good, and then display it after selecting the picture
               gvPic.setVisibility(View.VISIBLE);
               //Sets the maximum number of columns for the grid layout
               gvPic.setNumColumns(list.size());
            }
        }
    }

Of course, the custom adapter is used for grid layout. The BaseAdapter code is as follows:

private void loadAdapter(ArrayList<String> paths){
	//First, empty the collection of picture paths
        if (imagePaths!=null&& imagePaths.size()>0){
            imagePaths.clear();
        }
    //Also clear the useless data in paths
        if (paths.contains("000000")){
            paths.remove("000000");
        }
//        paths.add("000000");
        imagePaths.addAll(paths);
        adapter=new GridAdapter(imagePaths,MainActivity.this);
//        adapter.notifyDataSetChanged();
        gvPic.setAdapter(adapter);
    }

    private class GridAdapter extends BaseAdapter{
    //Create entry parameters, data list, layout filler, context content
        private ArrayList<String> listUrls;
        private LayoutInflater inflater;
        private Context context;
        public GridAdapter(ArrayList<String> listUrls,Context context){
            this.listUrls=listUrls;
            this.context=context;
            inflater=LayoutInflater.from(MainActivity.this);
        }
     //How many items can I get list data
        @Override
        public int getCount() {
            return listUrls.size();
        }
	//Get a piece of data in the list
        @Override
        public Object getItem(int position) {
            return listUrls.get(position);
        }
	//Gets the location of a piece of data in the list
        @Override
        public long getItemId(int position) {
            return position;
        }
	//Draw list
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
        //Materialized view holder class
            ViewHolder holder=null;
            //Empty content view
            if (convertView==null){
                holder=new ViewHolder();
                //Fill content view (R.layout.add_pic_item is a sub layout of grid layout)
                convertView=inflater.inflate(R.layout.add_pic_item,parent,false);
                //Assign the control id in the content view to the control in the view holder
                holder.image=(ImageView) convertView.findViewById(R.id.img_pic);
                holder.image.setBackground(getResources().getDrawable(R.drawable.add_picture_bg));
                convertView.setTag(holder);
            }else {
                holder= (ViewHolder) convertView.getTag();
            }
//Arrange the picture in the view
            final String path=listUrls.get(position);
            if (path.equals("000000")){
//                    holder.image.setImageResource(R.mipmap.ic_launcher);
//                listUrls.clear();
                listUrls.remove("000000");
            }else{
            //Load pictures through Glide picture caching mechanism framework
                Glide.with(MainActivity.this)
                        .load(path)
                        .placeholder(R.mipmap.ic_launcher_round)
                        .error(R.mipmap.ic_launcher_round)
                        .centerCrop()
                        .crossFade()
                        .into(holder.image);
                   //If the number of pictures reaches 3, hide the Add button to control user behavior
                if(listUrls.size()==3){
//                Toast.makeText(context), "only 3 pieces can be selected!", Toast.LENGTH_SHORT).show();
                    btnAddPic.setVisibility(View.GONE);
                }
            }
            return convertView;
        }
        //Create view holder class
        class ViewHolder{
            ImageView image;
        }
    }

Points to note when writing java code:

1. When writing java code, add the dependency of "multiple images selector" multi graph selector in the project:
2. When opening the picture selector, you need to open the permission to read the system album and network access.
3. Add a picture selector.

//In build Add to the gradle file, and then synchronize the project
implementation 'com.zfdang.multiple-images-selector:multiple-images-selector:1.1.5'
//Because it is the image displayed by the glide image loader, you also need to add glide dependencies
implementation 'com.github.bumptech.glide:glide:3.7.0'
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
//The following two permissions are the permissions you want to take pictures in the picture selector
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
	 ...
    android:name=".DemoApplication"
    ...
    
    <activity
        android:name="com.zfdang.multiple_images_selector.ImagesSelectorActivity"
        android:configChanges="orientation|screenSize"/>
    
</application>

Of course, Android 6 After version 0, you also need to manually add the permission to read the album when the program runs. The code is as follows:

 //Higher version manual access
    public void myPermission() {
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(
                    this,
                    PERMISSIONS_STORAGE,
                    IMAGE_OPEN
            );
        }
    }

    //Request callback
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull  String[] permissions, @NonNull  int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case IMAGE_OPEN:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
                    isGreen = false;
                } else {
                    isGreen = true;
                }
                break;
        }
    }
  1. Application initialization
public class DemoApplication extends Application
{
	@Override
	public void onCreate() {
    super.onCreate();

    // the following line is important
    Fresco.initialize(getApplicationContext());
	}
}

The above is a native framework of the multi graph selector I used in the company's project. It is relatively simple and easy to understand, and suitable for junior developers. Let's see the source code document of the framework github: Click here to enter the portal

Of course, this is not the only framework in android. Let's continue:

Detailed explanation of the use of Matisse picture selector
1, Introduction

  • Easy to use
  • Support day and night mode, and you can customize the theme
  • Customize the maximum number of selected pictures
  • Display of JPEG, PNG, GIF picture types
  • Supports the orderly selection of pictures, that is, when selecting pictures, there will be 1, 2, 3, 4... Style checkboxes;
  • Support user-defined filtering and completely customize filtering rules
  • You can define the zoom scale of the picture thumbnail.
  • Support horizontal and vertical screens. Matisse did the state saving
  • Different picture loading libraries are supported. Currently, glass and Picasso are supported

2, Use steps
1. Add dependency
build. Code in gradle file:

repositories {
    jcenter()
}
dependencies {
    compile 'com.zhihu.android:matisse:0.5.0-alpha4'
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

2. Add related permissions

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
//android6. The requirements for permission application of version 0 are the same as those of multiple images selector

3. Start selector

If you don't need to click to take photos and have no requirements for filtering, you can use it directly
intent jump to MatisseActivity from your current Activity or Fragment

Basic usage
1. Define constants

private static final int REQUEST_CODE_CHOOSE = 23;//Define request code constants

2. Start MatisseActivity

Matisse  
.from(MainActivity.this)
.choose(MimeType.allOf())//Show all photos and videos
.countable(true)//Select pictures in order
.maxSelectable(9)//The maximum number of choices is 9
.gridExpectedSize(120)//The picture shows the size of the table getResources()
        .getDimensionPixelSize(R.dimen.grid_expected_size)
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)//Select the desired orientation for the image and preview activity.
.thumbnailScale(0.85f)//Scale
.theme(R.style.Matisse_Zhihu)//Theme dark theme r.style Matisse_ Dracula
.imageEngine(new GlideEngine())//Loading mode
.forResult(REQUEST_CODE_CHOOSE);//Request code

3. Accept the result of the selection

  List<Uri> mSelected;
@Override      //Receive returned address
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {
        mSelected = Matisse.obtainResult(data);
        Log.d("Matisse", "mSelected: " + mSelected);
    }
}

Of course, if you want to try to implement photography and filter, you can refer to this Daniel's blog , (click to have a surprise)

Well, finally, let's take a look at the operation effect:

Picture selector operation effect

Well, the above is the content of this article. It's all the native framework of android. You can use it. Just understand the logic. Of course, my introduction is also very simple and easy to understand, because it's just to select pictures and display them. The logic is easy to understand. Of course, if you want to further explore the processing and cutting of pictures, you can refer to: Click to enter the new world

After such a long time to write a blog again, I'm a little hand-made. I should get familiar with it as soon as possible!!!

Topics: Java Android Design Pattern