Search code examples
javaandroidandroid-recyclerviewandroid-cardviewlinearlayoutmanager

How to make a RecylerView like WhatsApp Chat activity?


I building an app which should have a layout almost identical to the one used in WhatsApp in the chat activity. it should have a text input, camera & video recording option. Items should be centered inside the recycler and when the dataset is changed the recycler should scroll to the last item added. when the user clicks on the Editext keyboard should resize the view moving recycler up and focus on the last item in the Dataset.

For the scrolling part, I have used SmoothScrollToPosiotion but when an item, which contains an image or a video, the view gets chopped, auto scroll also stops working. a weird bug is when I open the keyboard by clicking on editText the recycler scrolls almost to the last item but not completely, only if reopen the keyboard multiple times it shows the item completely.

already tried solutions found on google, and here with not expected results :(


EDIT: almost solved, I have just moved SmoothScrollToposition inside the onClickListener of every button. Now it does not scroll to the last item it just scrolls to the item before the last but completely :D

here some code: (if needed full code at https://github.com/MikeSys/ChatApp)


MainActivity Layout

<android.support.constraint.ConstraintLayout   
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">


<android.support.v7.widget.RecyclerView
    android:background="@drawable/sfondo"
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/linearLayout"
    app:layout_constraintTop_toTopOf="parent"
    />


<LinearLayout
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="55dp"
    app:layout_constraintBottom_toBottomOf="parent"
    android:orientation="horizontal">

    <EditText
        android:layout_gravity="center_vertical"
        android:id="@+id/editText"
        android:layout_width="255dp"
        android:layout_height="55dp"
        android:background="#0003A9F4"
        android:hint="Scrivi"
        android:padding="10dp" />




    <Button
        android:layout_gravity="center_vertical"
        android:id="@+id/camera"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@drawable/camera"
      />

    <Button
        android:layout_gravity="center_vertical"
        android:id="@+id/video"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@drawable/video"
        />

    <Button
        android:id="@+id/send"
        android:layout_width="55dp"
        android:layout_height="55dp"
        android:background="@drawable/send" />


</LinearLayout>


</android.support.constraint.ConstraintLayout>

MainActivity Code

public class MainActivity extends AppCompatActivity {

private ArrayList<ModelloDati> dati = new ArrayList<>();
private LinearLayoutManager linearLayoutManager;
private static final String VIDEO_DIRECTORY = "/Chat";
private myAdapter adapter;
public  Uri passUri;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    // Variables-----------------------------------------

    final RecyclerView recyclerView = findViewById(R.id.recyclerView);
    Button video = findViewById(R.id.video);
    Button camera = findViewById(R.id.camera);
    Button send = findViewById(R.id.send);
    final EditText editText = findViewById(R.id.editText);


    // Layout Manager------------------------------------------------

    linearLayoutManager = new LinearLayoutManager(MainActivity.this);
    linearLayoutManager.setReverseLayout(true);
    recyclerView.setLayoutManager(linearLayoutManager);


    // Adapter-----------------------------------------

    if(dati.size()> 1){
        adapter =  new myAdapter(dati, this);
        //Removed SmoothScroll form here
        adapter.notifyDataSetChanged();
        recyclerView.setAdapter(adapter);

    }

    else{
        Collections.reverse(dati);
        adapter =  new myAdapter(dati,this);
        recyclerView.setAdapter(adapter);
    }


    // Click Listener Video button---------------------------------------------
    video.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
            startActivityForResult(intent,0);
            //Added here SmotthScroll
            recyclerView.smoothScrollToPosition(dati.size());
        }
    });

    // Click Listener Camera button--------------------------------------------
    camera.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(intent,1);
            //Added here SmotthScroll
            recyclerView.smoothScrollToPosition(dati.size());
        }
    });

    // Click Listener Send button----------------------------------------------
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String string = editText.getText().toString();
            dati.add(new ModelloDati(0,string));
            editText.getText().clear();
            //Added here SmotthScroll
            recyclerView.smoothScrollToPosition(dati.size());
            closeKeyboard();
        }
    });



}

private void closeKeyboard() {
    View view = getCurrentFocus();
    if(view != null){
        InputMethodManager imm =  
(InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(),0);
    }
}


@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable 
Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode){
        case 0:
            try {
                Uri contentURI = data.getData();
                passUri = contentURI;
                String recordedVideoPath = getPath(contentURI);
                Log.d("frrr", recordedVideoPath);
                saveVideoToInternalStorage(recordedVideoPath);
                dati.add(new ModelloDati(2, contentURI));
            }catch (Throwable o){Log.i("CAM","User aborted action");}
        case 1:
            try {
                Bitmap bitmap = (Bitmap)data.getExtras().get("data");
                dati.add(new ModelloDati(1,bitmap));
            }catch(Throwable o){
                Log.i("CAM","User aborted action");
            }
    }


}

private void saveVideoToInternalStorage (String filePath) {

    File new file;

    try {

        File currentFile = new File(filePath);
        File wallpaperDirectory = new 
File(Environment.getExternalStorageDirectory() + VIDEO_DIRECTORY);
        newfile = new File(wallpaperDirectory, 
Calendar.getInstance().getTimeInMillis() + ".mp4");

        if (!wallpaperDirectory.exists()) {
            wallpaperDirectory.mkdirs();
        }

        if(currentFile.exists()){

            InputStream in = new FileInputStream(currentFile);
            OutputStream out = new FileOutputStream(newfile);

            // Copy the bits from instream to outstream
            byte[] buf = new byte[1024];
            int len;

            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
            Log.v("vii", "Video file saved successfully.");
        }else{
            Log.v("vii", "Video saving failed. Source file missing.");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

}



public String getPath(Uri uri) {
    String[] projection = { MediaStore.Video.Media.DATA };
    Cursor cursor = getContentResolver().query(uri, projection, null, null, 
null);
    if (cursor != null) {
        // HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
        // THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
        int column_index = cursor
                .getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } else
        return null;
}




}

Solution

  • Solved by Moving smoothScrollToPosition inside onClickListerner (video/camera button I had to move it inside onActivityResult).


    Updated Main

    public class MainActivity extends AppCompatActivity {
    
    private ArrayList<ModelloDati> dati = new ArrayList<>();
    private LinearLayoutManager linearLayoutManager;
    private static final String VIDEO_DIRECTORY = "/Chat";
    private myAdapter adapter;
    private RecyclerView recyclerView;
    private VideoView videoView;
    public  Uri passUri;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
    
        // Variables-----------------------------------------
    
         recyclerView = findViewById(R.id.recyclerView);
        Button video = findViewById(R.id.video);
        Button camera = findViewById(R.id.camera);
        Button send = findViewById(R.id.send);
        final EditText editText = findViewById(R.id.editText);
    
    
        // Layout Manager------------------------------------------------
    
        linearLayoutManager = new LinearLayoutManager(MainActivity.this);
        linearLayoutManager.setStackFromEnd(true);
        RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setItemAnimator(itemAnimator);
    
    
        // Adapter-----------------------------------------
    
            //adapter.notifyDataSetChanged();
            adapter =  new myAdapter(dati, this);
            recyclerView.setAdapter(adapter);
    
    
    
        // Click Listener Video button----------------------------------------------
        video.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                startActivityForResult(intent,0);
            }
        });
    
        // Click Listener Camera button----------------------------------------------
        camera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent,1);
            }
        });
    
        // Click Listener Send button------------------------------------------------
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String string = editText.getText().toString();
                dati.add(new ModelloDati(0,string));
                adapter.notifyItemInserted(dati.size());
                editText.getText().clear();
                recyclerView.smoothScrollToPosition(dati.size());
                closeKeyboard();
            }
        });
    
    
    }
    
    private void closeKeyboard() {
        View view = getCurrentFocus();
        if(view != null){
            InputMethodManager imm = 
    (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(),0);
        }
    }
    
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)  
    {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case 0:
                try {
                    Uri contentURI = data.getData();
                    passUri = contentURI;
                    String recordedVideoPath = getPath(contentURI);
                    saveVideoToInternalStorage(recordedVideoPath);
                    dati.add(new ModelloDati(2, contentURI));
                    adapter.notifyItemInserted(dati.size());
                    recyclerView.smoothScrollToPosition(dati.size());
    
                }catch (Throwable o){Log.i("CAM","User aborted action");}
            case 1:
                try {
                    Bitmap bitmap = (Bitmap)data.getExtras().get("data");
                    dati.add(new ModelloDati(1,bitmap));
                    adapter.notifyItemInserted(dati.size());
                    recyclerView.smoothScrollToPosition(dati.size());
    
    
                }catch(Throwable o){
                    Log.i("CAM","User aborted action");
                }
    
        }
    
    
    }
    
    
    
    private void saveVideoToInternalStorage (String filePath) {
    
        File newfile;
    
        try {
    
            File currentFile = new File(filePath);
            File wallpaperDirectory = new File(Environment.getExternalStorageDirectory() + 
    VIDEO_DIRECTORY);
            newfile = new File(wallpaperDirectory, Calendar.getInstance().getTimeInMillis()  
     + ".mp4");
    
            if (!wallpaperDirectory.exists()) {
                wallpaperDirectory.mkdirs();
            }
    
            if(currentFile.exists()){
    
                InputStream in = new FileInputStream(currentFile);
                OutputStream out = new FileOutputStream(newfile);
    
                // Copy the bits from instream to outstream
                byte[] buf = new byte[1024];
                int len;
    
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
                in.close();
                out.close();
                Log.v("vii", "Video file saved successfully.");
            }else{
                Log.v("vii", "Video saving failed. Source file missing.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    }
    
    
    
    public String getPath(Uri uri) {
        String[] projection = { MediaStore.Video.Media.DATA };
        Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
        if (cursor != null) {
            // HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
            // THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
            int column_index = cursor
                    .getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        } else
            return null;
    }
    
    
    }