Skip to content

Instantly share code, notes, and snippets.

@MohammadSamandari
Created April 2, 2020 19:55
Show Gist options
  • Save MohammadSamandari/5a081bf555143e907a2f00f4009984c5 to your computer and use it in GitHub Desktop.
Save MohammadSamandari/5a081bf555143e907a2f00f4009984c5 to your computer and use it in GitHub Desktop.
Android Cardview - Color - Recyclerview -
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.android.materialme.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:onClick="resetSports"
android:src="@drawable/ic_reset"
android:tint="#FFFFFF"
android:focusable="true" />
</RelativeLayout>

Cards and Color

Cardview

When presenting information that has mixed media (like images and text), the Material Design guidelines recommend using a CardView, which is a FrameLayout with some extra features (such as elevation and rounded corners) that give it a consistent look and feel across many different applications and platforms. CardView is a UI component found in the Android Support Libraries.

Glide

Using images is resource intensive for your app: the Android framework has to load the entire image into memory at full resolution, even if the app only displays a small thumbnail of the image.

In this section you learn how to use the Glide library to load large images efficiently, without draining your resources or even crashing your app due to 'Out of Memory' exceptions.

  1. we added some picture to the drawable folder of the project.
  2. we created an array of strings in the string.xml so that each item refers to a drawable resource.
  3. we added the new image resource to the sport object. we edited the sport class for that.
  4. fixed the initializeData() method because the constructor for the Sport object demands the image resource as the third parameter.
  • A convenient data structure to use would be a TypedArray. A TypedArray allows you to store an array of other XML resources. Using a TypedArray, you can obtain the image resources as well as the sports title and information by using indexing in the same loop.
TypedArray sportsImageResources = 
        getResources().obtainTypedArray(R.array.sports_images);
  • You can access an element at index i in the TypedArray by using the appropriate "get" method, depending on the type of resource in the array. In this specific case, it contains resource IDs, so you use the getResourceId() method.
  1. Clean up the data in the typed array once you have created the Sport data ArrayList:
sportImageResources.recycle();
  • The adjustViewBounds attribute makes the ImageView adjust its boundaries to preserve the aspect ratio of the image.
  1. Load the images using Glide
  • There are a number of ways to reduce the memory consumption when loading images, but one of the easiest approaches is to use an Image Loading Library like Glide, which you will do in this step. Glide uses background processing, as well some other complex processing, to reduce the memory requirements of loading images. It also includes some useful features like showing placeholder images while the desired images are loaded. we edited the adapter so that it loads the image into imageview using the glide library.

Make your CardView swipeable, movable, and clickable

The Android SDK includes a class called ItemTouchHelper that is used to define what happens to RecyclerView list items when the user performs various touch actions, such as swipe, or drag and drop. Some of the common use cases are already implemented in a set of methods in ItemTouchHelper.SimpleCallback.

Swipable Recyclerview

  1. we created a ItemTouchHelper like this in the onCreate of the main activity.
        ItemTouchHelper helper=new ItemTouchHelper(
                new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
                        ,ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
            @Override
            public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped (@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                mSportsData.remove(viewHolder.getAdapterPosition());
                mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
            }
        });
  1. we attached the recyclerview with the hellper
helper.attachToRecyclerView(mRecyclerView);

Drag and drop

  1. declare the directions that we want this move to happen
  2. In the onMove() method, get the original and target index from the second and third argument passed in (corresponding to the original and target view holders).
  3. Swap the items in the dataset by calling Collections.swap() and pass in the dataset, and the initial and final indexes:
  4. Notify the adapter that the item was moved, passing in the old and new indexes, and change the return statement to true:
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/sportsImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/title_placeholder"
android:layout_alignBottom="@id/sportsImage"
android:theme="@style/ThemeOverlay.AppCompat.Dark"/>
<TextView
android:id="@+id/newsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/news_placeholder"
android:layout_below="@id/sportsImage"
android:textColor="?android:textColorSecondary"/>
<TextView
android:id="@+id/subTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/sports_info_placeholder"
android:layout_below="@id/newsTitle"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
/*
* Copyright (C) 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.materialme;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/***
* Main Activity for the Material Me app, a mock sports news application
* with poor design choices.
*/
public class MainActivity extends AppCompatActivity {
private static String TAG = "Lord";
// Member variables.
private RecyclerView mRecyclerView;
private ArrayList<Sport> mSportsData;
private SportsAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the RecyclerView.
mRecyclerView = findViewById(R.id.recyclerView);
// Set the Layout Manager.
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// Initialize the ArrayList that will contain the data.
mSportsData = new ArrayList<>();
// Initialize the adapter and set it to the RecyclerView.
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
// Get the data.
initializeData();
ItemTouchHelper helper=new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
,ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(mSportsData, from, to);
mAdapter.notifyItemMoved(from, to);
return true;
}
@Override
public void onSwiped (@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mSportsData.remove(viewHolder.getAdapterPosition());
mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
});
helper.attachToRecyclerView(mRecyclerView);
}
/**
* Initialize the sports data from resources.
*/
private void initializeData() {
// Get the resources from the XML file.
String[] sportsList = getResources()
.getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources()
.getStringArray(R.array.sports_info);
TypedArray sportsImageResources =
getResources().obtainTypedArray(R.array.sports_images);
// Clear the existing data (to avoid duplication).
mSportsData.clear();
// Create the ArrayList of Sports objects with titles and
// information about each sport.
for(int i=0;i<sportsList.length;i++){
mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsImageResources.getResourceId(i, 0)));
}
//Clean up the data in the typed array once you have created the Sport data ArrayList:
sportsImageResources.recycle();
// Notify the adapter of the change.
mAdapter.notifyDataSetChanged();
}
public void resetSports (View view) {
initializeData();
}
@Override
public void onSaveInstanceState (@NonNull Bundle outState) {
String[] sportsTitle = new String[mSportsData.size()];
String[] sportsInfo = new String[mSportsData.size()];
int[] sportImageResources = new int[mSportsData.size()];
for (int i = 0; i < mSportsData.size(); i++) {
sportsTitle[i] = mSportsData.get(i).getTitle();
sportsInfo[i] = mSportsData.get(i).getInfo();
sportImageResources[i] = mSportsData.get(i).getImageResource();
}
outState.putStringArray("sportsTitle", sportsTitle);
outState.putStringArray("sportsInfo", sportsInfo);
outState.putIntArray("sportsImageResources", sportImageResources);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState (@NonNull Bundle savedInstanceState) {
mSportsData.clear();
String[] sportsTitle = savedInstanceState.getStringArray("sportsTitle");
String[] sportsInfo = savedInstanceState.getStringArray("sportsInfo");
int[] sportImageResources = savedInstanceState.getIntArray("sportsImageResources");
for (int i = 0; i < sportsTitle.length; i++) {
mSportsData.add(new Sport(sportsTitle[i], sportsInfo[i], sportImageResources[i]));
mAdapter.notifyDataSetChanged();
}
super.onRestoreInstanceState(savedInstanceState);
}
}
/*
* Copyright (C) 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.materialme;
/**
* Data model for each row of the RecyclerView
*/
class Sport {
// Member variables representing the title and information about the sport.
private String title;
private String info;
private int imageResource;
/**
* Constructor for the Sport data model.
*
* @param title The name if the sport.
* @param info Information about the sport.
*/
Sport(String title, String info, int imageResource) {
this.title = title;
this.info = info;
this.imageResource = imageResource;
}
/**
* Gets the title of the sport.
*
* @return The title of the sport.
*/
String getTitle() {
return title;
}
/**
* Gets the info about the sport.
*
* @return The info about the sport.
*/
String getInfo() {
return info;
}
int getImageResource() { return imageResource;}
}
/*
* Copyright (C) 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.materialme;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
/***
* The adapter class for the RecyclerView, contains the sports data.
*/
class SportsAdapter extends RecyclerView.Adapter<SportsAdapter.ViewHolder> {
// Member variables.
private ArrayList<Sport> mSportsData;
private Context mContext;
/**
* Constructor that passes in the sports data and the context.
*
* @param sportsData ArrayList containing the sports data.
* @param context Context of the application.
*/
SportsAdapter(Context context, ArrayList<Sport> sportsData) {
this.mSportsData = sportsData;
this.mContext = context;
}
/**
* Required method for creating the viewholder objects.
*
* @param parent The ViewGroup into which the new View will be added
* after it is bound to an adapter position.
* @param viewType The view type of the new View.
* @return The newly created ViewHolder.
*/
@Override
public SportsAdapter.ViewHolder onCreateViewHolder(
ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext).
inflate(R.layout.list_item, parent, false));
}
/**
* Required method that binds the data to the viewholder.
*
* @param holder The viewholder into which the data should be put.
* @param position The adapter position.
*/
@Override
public void onBindViewHolder(SportsAdapter.ViewHolder holder,
int position) {
// Get current sport.
Sport currentSport = mSportsData.get(position);
// Populate the textviews with data.
holder.bindTo(currentSport);
}
/**
* Required method for determining the size of the data set.
*
* @return Size of the data set.
*/
@Override
public int getItemCount() {
return mSportsData.size();
}
/**
* ViewHolder class that represents each row of data in the RecyclerView.
*/
class ViewHolder extends RecyclerView.ViewHolder {
// Member Variables for the TextViews
private TextView mTitleText;
private TextView mInfoText;
private ImageView mSportsImage;
/**
* Constructor for the ViewHolder, used in onCreateViewHolder().
*
* @param itemView The rootview of the list_item.xml layout file.
*/
ViewHolder(View itemView) {
super(itemView);
// Initialize the views.
mTitleText = itemView.findViewById(R.id.title);
mInfoText = itemView.findViewById(R.id.subTitle);
mSportsImage = itemView.findViewById(R.id.sportsImage);
}
void bindTo(Sport currentSport){
// Populate the textviews with data.
mTitleText.setText(currentSport.getTitle());
mInfoText.setText(currentSport.getInfo());
Glide.with(mContext).load(currentSport.getImageResource()).into(mSportsImage);
}
}
}
<!--
Copyright 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">Material Me!</string>
<string name="title_placeholder">Title</string>
<string name="news_placeholder">News</string>
<string name="sports_info_placeholder">Here is some news</string>
<string-array name="sports_titles">
<item>Baseball</item>
<item>Badminton</item>
<item>Basketball</item>
<item>Bowling</item>
<item>Cycling</item>
<item>Golf</item>
<item>Running</item>
<item>Soccer</item>
<item>Swimming</item>
<item>Table Tennis</item>
<item>Tennis</item>
</string-array>
<string-array name="sports_info">
<item>Here is some Baseball news!</item>
<item>Here is some Badminton news!</item>
<item>Here is some Basketball news!</item>
<item>Here is some Bowling news!</item>
<item>Here is some Cycling news!</item>
<item>Here is some Golf news!</item>
<item>Here is some Running news!</item>
<item>Here is some Soccer news!</item>
<item>Here is some Swimming news!</item>
<item>Here is some Table Tennis news!</item>
<item>Here is some Tennis news!</item>
</string-array>
<array name="sports_images">
<item>@drawable/img_baseball</item>
<item>@drawable/img_badminton</item>
<item>@drawable/img_basketball</item>
<item>@drawable/img_bowling</item>
<item>@drawable/img_cycling</item>
<item>@drawable/img_golf</item>
<item>@drawable/img_running</item>
<item>@drawable/img_soccer</item>
<item>@drawable/img_swimming</item>
<item>@drawable/img_tabletennis</item>
<item>@drawable/img_tennis</item>
</array>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment