This example will tell you how to create a subclass of SwipeRefreshLayout to change the default refresh behavior from the pull-down to pull to left or right. This example will also use RecyclerView. And the RecyclerView should be the direct child of the custom SwipeRefreshLayout object.
1. Change SwipeRefreshLayout Refresh Behavior Example Overview.
If you can not watch the above video, you can see it on the youtube URL https://youtu.be/4uCpI7e19Ko
- There is a RecyclerView on the screen, it has 6 chid ImageView objects.
- When pull to the right from the beginning, it will show the SwipeRefreshLayout indicator.
- After data load, a new image will be added at the beginning. When you scroll to the end and pull to left, it will add a new image at the RecyclerView ending.
- Below is the java files used in this example.
D:\WORK\DEV2QA.COM-EXAMPLE-CODE\ANDROIDEXAMPLEPROJECT\EXAMPLE\APP\SRC\MAIN\JAVA\COM\DEV2QA\EXAMPLE\MATERIAL_DESIGN\SWIPE_REFRESH_LAYOUT CustomSwipeRecyclerViewDataAdapter.java CustomSwipeRecyclerViewHolder.java CustomSwipeRecyclerViewItem.java CustomSwipeRefreshLayout.java CustomSwipeRefreshLayoutActivity.java CustomSwipeRefreshLoadMoreListener.java
- You also need to add the below dependency library in your build.gradle file.
compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support:design:26.+' compile 'com.android.support:cardview-v7:26.+' compile 'com.android.support:recyclerview-v7:26.+'
2. Main Activity.
- CustomSwipeRefreshLayoutActivity.java
package com.dev2qa.example.material_design.swipe_refresh_layout; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import com.dev2qa.example.R; import java.util.ArrayList; import java.util.List; public class CustomSwipeRefreshLayoutActivity extends AppCompatActivity { private RecyclerView recyclerView = null; private CustomSwipeRecyclerViewDataAdapter recyclerViewDataAdapter = null; public List<CustomSwipeRecyclerViewItem> recyclerViewItemList = null; private CustomSwipeRefreshLayout customSwipeRefreshLayout = null; public Handler uiUpdateHandler = null; public int MESSAGE_UPDATE_RECYCLER_VIEW = 1; public String MESSAGE_KEY_SCROLL_TO_BEGINNING = "MESSAGE_KEY_SCROLL_TO_BEGINNING"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_swipe_refresh_layout); setTitle("dev2qa.com - Android Custom Swipe Refresh Layout Example."); initControls(); CustomSwipeRefreshLoadMoreListener loadMoreListener = new CustomSwipeRefreshLoadMoreListener(this); customSwipeRefreshLayout.setLoadMoreListener(loadMoreListener); } private void initControls() { if(recyclerView==null) { // Get RecyclerView. recyclerView = (RecyclerView)findViewById(R.id.custom_swipe_refresh_layout_recycler_view); // Create the RecyclerView items list. recyclerViewItemList = new ArrayList<CustomSwipeRecyclerViewItem>(); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_audi)); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_benz)); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_bmw)); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_future)); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_land_rover)); recyclerViewItemList.add(new CustomSwipeRecyclerViewItem(R.drawable.car_jeep)); // Create the linear layout manager. LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // Use above layout manager for RecyclerView.. recyclerView.setLayoutManager(linearLayoutManager); // Create recycler view data adapter with item list. recyclerViewDataAdapter = new CustomSwipeRecyclerViewDataAdapter(recyclerViewItemList); // Set RecyclerView data adapter. recyclerView.setAdapter(recyclerViewDataAdapter); } if(customSwipeRefreshLayout==null) { customSwipeRefreshLayout = (CustomSwipeRefreshLayout)findViewById(R.id.custom_swipe_refresh_layout); } if(uiUpdateHandler == null) { uiUpdateHandler = new Handler() { @Override public void handleMessage(Message msg) { // If the message want to refresh list view. if(msg.what == MESSAGE_UPDATE_RECYCLER_VIEW) { // Refresh list view after add item data. recyclerViewDataAdapter.notifyDataSetChanged(); Bundle bundle = msg.getData(); boolean scrollToBeginning = bundle.getBoolean(MESSAGE_KEY_SCROLL_TO_BEGINNING); if(scrollToBeginning) { recyclerView.scrollToPosition(0); }else { recyclerView.scrollToPosition(recyclerViewItemList.size() - 1); } } // Stop showing the swipe refresh layout. customSwipeRefreshLayout.setRefreshing(false); customSwipeRefreshLayout.setLoading(false); } }; } } }
3. Custom SwipeRefreshLayout Java File.
- CustomSwipeRefreshLayout.java
package com.dev2qa.example.material_design.swipe_refresh_layout; import android.content.Context; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public class CustomSwipeRefreshLayout extends SwipeRefreshLayout { // Save X position in pixels when user touch press down. private float pressDownX; // Save X position in pixels when user touch release up. private float pressUpX; private int scaledTouchSlop = 0; private RecyclerView recyclerView = null; private boolean loading = false; private CustomSwipeRefreshLoadMoreListener loadMoreListener; public boolean isLoading() { return loading; } public void setLoading(boolean loading) { this.loading = loading; if(this.loading) { // Show progress dialog. this.setRefreshing(true); }else { // Hide progress dialog this.setRefreshing(false); // Clear old press X value. pressUpX = 0; pressDownX = 0; } } public CustomSwipeRefreshLoadMoreListener getLoadMoreListener() { return loadMoreListener; } public void setLoadMoreListener(CustomSwipeRefreshLoadMoreListener loadMoreListener) { this.loadMoreListener = loadMoreListener; } public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); // Get ViewConfiguration object. ViewConfiguration viewConfiguration = ViewConfiguration.get(context); // Get minimum distance for touch move action. scaledTouchSlop = viewConfiguration.getScaledTouchSlop(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if(recyclerView == null) { int childCount = getChildCount(); if(childCount > 0) { for( int i=0;i<childCount;i++) { View firstChild = getChildAt(i); if (firstChild instanceof RecyclerView) { recyclerView = (RecyclerView) firstChild; recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (canLoadMoreData()) { loadMoreData(); } } }); break; } } } } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int pressAction = ev.getAction(); float xPosition = ev.getX(); if(pressAction == MotionEvent.ACTION_UP) { // If press action up. pressUpX = xPosition; }else if(pressAction == MotionEvent.ACTION_DOWN) { // If press action down. pressDownX = xPosition; }else if(pressAction == MotionEvent.ACTION_MOVE) { // If press action move. if(canLoadMoreData()) { loadMoreData(); } } return super.dispatchTouchEvent(ev); } private boolean canLoadMoreData() { boolean ret = false; // If just loading, then can not load more data again. if(loading) { ret = false; }else { float deltaX = pressDownX - pressUpX; LinearLayoutManager recyclerViewLayoutManager = null; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if(layoutManager instanceof LinearLayoutManager) { recyclerViewLayoutManager = (LinearLayoutManager)layoutManager; } int firstCompleteVisibleItemPosition = recyclerViewLayoutManager.findFirstCompletelyVisibleItemPosition(); int lastCompleteVisibleItemPosition = recyclerViewLayoutManager.findLastCompletelyVisibleItemPosition(); int recyclerViewItemCount = recyclerView.getAdapter().getItemCount(); float deltaXAbs = Math.abs(deltaX); if(deltaXAbs > scaledTouchSlop) { if(deltaX > 0) { // If scroll from right to left at RecyclerView endding. if(lastCompleteVisibleItemPosition==(recyclerViewItemCount - 1)) { ret = true; } }else { // If scroll from left to right at RecyclerView beginning. if (firstCompleteVisibleItemPosition == 0) { ret = true; } } } } return ret; } private void loadMoreData() { if (loadMoreListener != null) { setLoading(true); boolean insertBeginning = true; if(pressDownX > pressUpX) { insertBeginning = false; } loadMoreListener.loadMoreData(insertBeginning); } } }
4. Custom RecyclerView Load More Listener.
- CustomSwipeRefreshLoadMoreListener.java
package com.dev2qa.example.material_design.swipe_refresh_layout; import android.os.Bundle; import android.os.Message; import com.dev2qa.example.R; public class CustomSwipeRefreshLoadMoreListener { private CustomSwipeRefreshLayoutActivity customSwipeRefreshLayoutActivity; public CustomSwipeRefreshLoadMoreListener(CustomSwipeRefreshLayoutActivity customSwipeRefreshLayoutActivity) { this.customSwipeRefreshLayoutActivity = customSwipeRefreshLayoutActivity; } public void loadMoreData(final boolean insertBeginning) { // Start a child thread to load data from the server. new Thread() { @Override public void run() { try { // Emulate read data from the server which will cost some time. Thread.sleep(3000); CustomSwipeRecyclerViewItem newItem = new CustomSwipeRecyclerViewItem(R.drawable.car_future); if(insertBeginning) { customSwipeRefreshLayoutActivity.recyclerViewItemList.add(0, newItem); }else { customSwipeRefreshLayoutActivity.recyclerViewItemList.add(newItem); } // Create a message object. Message message = new Message(); // Set message purpose. message.what = customSwipeRefreshLayoutActivity.MESSAGE_UPDATE_RECYCLER_VIEW; // Add new item insert position info. Bundle bundle = new Bundle(); bundle.putBoolean(customSwipeRefreshLayoutActivity.MESSAGE_KEY_SCROLL_TO_BEGINNING, insertBeginning); message.setData(bundle); // Send the message to main thread Handler to process. customSwipeRefreshLayoutActivity.uiUpdateHandler.sendMessage(message); }catch(InterruptedException ex) { ex.printStackTrace(); } } }.start(); } }
5. RecyclerView Data Adapter.
- CustomSwipeRecyclerViewDataAdapter.java
package com.dev2qa.example.material_design.swipe_refresh_layout; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.dev2qa.example.R; import java.util.List; public class CustomSwipeRecyclerViewDataAdapter extends RecyclerView.Adapter<CustomSwipeRecyclerViewHolder> { private List<CustomSwipeRecyclerViewItem> recyclerViewItemList; public CustomSwipeRecyclerViewDataAdapter(List<CustomSwipeRecyclerViewItem> recyclerViewItemList) { this.recyclerViewItemList = recyclerViewItemList; } @Override public CustomSwipeRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // Get LayoutInflater object. LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); // Inflate the RecyclerView item layout xml. final View itemView = layoutInflater.inflate(R.layout.activity_custom_swipe_refresh_layout_recycler_view_item, parent, false); // Create and return custom swipe refresh layout Recycler View Holder object. CustomSwipeRecyclerViewHolder ret = new CustomSwipeRecyclerViewHolder(itemView); return ret; } @Override public void onBindViewHolder(CustomSwipeRecyclerViewHolder holder, int position) { if(recyclerViewItemList!=null) { // Get item dto in list. CustomSwipeRecyclerViewItem viewItem = recyclerViewItemList.get(position); if(viewItem != null) { // Set car image resource id. holder.getItemImageView().setImageResource(viewItem.getItemImageId()); } } } @Override public int getItemCount() { int ret = 0; if(recyclerViewItemList!=null) { ret = recyclerViewItemList.size(); } return ret; } }
6. RecyclerView View Holder.
- CustomSwipeRecyclerViewHolder.java
package com.dev2qa.example.material_design.swipe_refresh_layout; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import com.dev2qa.example.R; public class CustomSwipeRecyclerViewHolder extends RecyclerView.ViewHolder { private ImageView itemImageView = null; public CustomSwipeRecyclerViewHolder(View itemView) { super(itemView); if(itemView != null) { itemImageView = (ImageView)itemView.findViewById(R.id.custom_swipe_refresh_layout_recycler_view_item_image_view); } } public ImageView getItemImageView() { return itemImageView; } }
7. RecyclerView View Item.
- CustomSwipeRecyclerViewItem.java
package com.dev2qa.example.material_design.swipe_refresh_layout; public class CustomSwipeRecyclerViewItem { private int itemImageId; public CustomSwipeRecyclerViewItem(int itemImageId) { this.itemImageId = itemImageId; } public int getItemImageId() { return itemImageId; } public void setItemImageId(int itemImageId) { this.itemImageId = itemImageId; } }
8. Main Activity Layout Xml File.
- Use the custom swipe refresh layout class in the activity definition layout XML file as below.
- activity_custom_swipe_refresh_layout.xml
<com.dev2qa.example.material_design.swipe_refresh_layout.CustomSwipeRefreshLayout android:id="@+id/custom_swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundGravity="center_vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/custom_swipe_refresh_layout_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </com.dev2qa.example.material_design.swipe_refresh_layout.CustomSwipeRefreshLayout>
9. RecyclerView Item Layout Xml File.
- activity_custom_swipe_refresh_layout_recycler_view_item.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" app:cardCornerRadius="10dp" app:cardElevation="8dp"> <ImageView android:id="@+id/custom_swipe_refresh_layout_recycler_view_item_image_view" android:layout_width="200dp" android:layout_height="200dp" android:scaleType="centerCrop"/> </android.support.v7.widget.CardView>
Reference