diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 69fe12a..38d2c8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,10 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="AllowBackup"> - + + + + diff --git a/app/src/main/java/com/test/MainActivity.java b/app/src/main/java/com/test/MainActivity.java new file mode 100644 index 0000000..63a06cd --- /dev/null +++ b/app/src/main/java/com/test/MainActivity.java @@ -0,0 +1,101 @@ +package com.test; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.test.helper.ItemClickHelperActivity; +import com.test.helper.R; +import com.test.helper.ScrollToPositionHelperActivity; +import com.test.helper.SelectableHelperActivity; + +import java.util.ArrayList; +import java.util.List; + +import recyclerview.helper.ItemClickHelper; +import recyclerview.helper.ItemClickableAdapter; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + List itemList = new ArrayList<>(); + + itemList.add(new TestListAdapter.Item("ItemClickHelper", ItemClickHelperActivity.class)); + itemList.add(new TestListAdapter.Item("SelectableHelper", SelectableHelperActivity.class)); + itemList.add(new TestListAdapter.Item("ScrollToPositionHelper", ScrollToPositionHelperActivity.class)); + + RecyclerView recyclerView = findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(new TestListAdapter(this, itemList)); + } + + private static class TestListAdapter extends ItemClickableAdapter { + private Context mContext; + private List mItemList; + + TestListAdapter(Context context, List itemList) { + mContext = context; + mItemList = new ArrayList<>(itemList); + + setOnItemClickListener(new ItemClickHelper.OnItemClickListener() { + @Override + public void onItemClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { + Intent intent = new Intent(mContext, mItemList.get(position).activity); + mContext.startActivity(intent); + } + }); + } + + @NonNull + @Override + public TestListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(android.R.layout.simple_list_item_1, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + super.onBindViewHolder(holder, position); + + holder.text1.setText(mItemList.get(position).title); + } + + @Override + public int getItemCount() { + return mItemList.size(); + } + + private static class ViewHolder extends RecyclerView.ViewHolder { + TextView text1; + + ViewHolder(@NonNull View itemView) { + super(itemView); + text1 = itemView.findViewById(android.R.id.text1); + } + } + + private static class Item { + String title; + Class activity; + + Item(String title, Class activity) { + this.title = title; + this.activity = activity; + } + } + } +} diff --git a/app/src/main/java/com/test/helper/ItemClickHelperActivity.java b/app/src/main/java/com/test/helper/ItemClickHelperActivity.java new file mode 100644 index 0000000..d3e64a4 --- /dev/null +++ b/app/src/main/java/com/test/helper/ItemClickHelperActivity.java @@ -0,0 +1,116 @@ +package com.test.helper; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import recyclerview.helper.ItemClickHelper; + +public class ItemClickHelperActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_item_click_helper); + + RecyclerView recyclerView = findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(new ItemClickHelperAdapter(this, 50)); + } + + public static class ItemClickHelperAdapter extends RecyclerView.Adapter { + private Context mContext; + private int mSize; + private ItemClickHelper mItemClickHelper; + + ItemClickHelperAdapter(Context context, int size) { + mContext = context; + mSize = size; + mItemClickHelper = new ItemClickHelper(); + + mItemClickHelper.setOnItemClickListener(new ItemClickHelper.OnItemClickListener() { + @Override + public void onItemClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { + switch (viewId) { + case R.id.item: + Toast.makeText(mContext, "ItemClicked, position: " + position, Toast.LENGTH_SHORT).show(); + break; + case R.id.button: + Toast.makeText(mContext, "ButtonClicked, position: " + position, Toast.LENGTH_SHORT).show(); + break; + } + } + }); + + mItemClickHelper.setOnItemLongClickListener(new ItemClickHelper.OnItemLongClickListener() { + @Override + public boolean onItemLongClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { + switch (viewId) { + case R.id.item: + Toast.makeText(mContext, "ItemLongClicked, position: " + position, Toast.LENGTH_SHORT).show(); + return true; + case R.id.button: + Toast.makeText(mContext, "ButtonLongClicked, position: " + position, Toast.LENGTH_SHORT).show(); + return true; + } + + return false; + } + }); + } + + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + mItemClickHelper.attachToRecyclerView(recyclerView); + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + super.onDetachedFromRecyclerView(recyclerView); + mItemClickHelper.detach(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_recycler, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + mItemClickHelper.bindClickListener(holder.itemView, holder.button); + mItemClickHelper.bindLongClickListener(holder.itemView, holder.button); + + holder.tvText.setText(String.valueOf(position)); + } + + @Override + public int getItemCount() { + return mSize; + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView tvText; + Button button; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + tvText = itemView.findViewById(R.id.tvText); + button = itemView.findViewById(R.id.button); + } + } + } +} diff --git a/app/src/main/java/com/test/helper/MainActivity.java b/app/src/main/java/com/test/helper/MainActivity.java deleted file mode 100644 index 9e76e68..0000000 --- a/app/src/main/java/com/test/helper/MainActivity.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.test.helper; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import android.os.Bundle; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - RecyclerView recyclerView = findViewById(R.id.recyclerView); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.setAdapter(new TestAdapter(this, 100)); - } -} diff --git a/app/src/main/java/com/test/helper/ScrollToPositionHelperActivity.java b/app/src/main/java/com/test/helper/ScrollToPositionHelperActivity.java new file mode 100644 index 0000000..3fe5aab --- /dev/null +++ b/app/src/main/java/com/test/helper/ScrollToPositionHelperActivity.java @@ -0,0 +1,94 @@ +package com.test.helper; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import recyclerview.helper.ScrollToPositionHelper; + +public class ScrollToPositionHelperActivity extends AppCompatActivity { + private ScrollToPositionHelper mScrollToPositionHelper; + + private static final int MAX_POSITION = 99; + private int mPosition = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scroll_to_position_helper); + + RecyclerView recyclerView = findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(new Adapter(MAX_POSITION + 1)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mScrollToPositionHelper = new ScrollToPositionHelper(recyclerView); + } + } + + public void onClick(View view) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + Toast.makeText(this, "Require API Level 16", Toast.LENGTH_SHORT).show(); + return; + } + + mPosition += 5; + + if (mPosition >= MAX_POSITION) { + mPosition = 0; + } + + switch (view.getId()) { + case R.id.btnScrollTo: + mScrollToPositionHelper.scrollToPosition(mPosition); + break; + case R.id.btnSmoothScrollTo: + mScrollToPositionHelper.smoothScrollToPosition(mPosition); + break; + } + } + + private static class Adapter extends RecyclerView.Adapter { + private int mSize; + + Adapter(int size) { + mSize = size; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(android.R.layout.simple_list_item_1, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.text1.setText(String.valueOf(position)); + } + + @Override + public int getItemCount() { + return mSize; + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView text1; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + text1 = itemView.findViewById(android.R.id.text1); + } + } + } +} diff --git a/app/src/main/java/com/test/helper/SelectableHelperActivity.java b/app/src/main/java/com/test/helper/SelectableHelperActivity.java new file mode 100644 index 0000000..c8ac0a5 --- /dev/null +++ b/app/src/main/java/com/test/helper/SelectableHelperActivity.java @@ -0,0 +1,205 @@ +package com.test.helper; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RadioGroup; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; + +import recyclerview.helper.ItemClickHelper; +import recyclerview.helper.SelectableAdapter; +import recyclerview.helper.SelectableHelper; + +public class SelectableHelperActivity extends AppCompatActivity { + private List mItems; + private TestSelectableAdapter mAdapter; + + private int mIndex; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_selectable_helper); + + mItems = new ArrayList<>(100); + for (int i = 0; i < 100; i++) { + mItems.add(String.valueOf(i)); + } + + RecyclerView recyclerView = findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + mAdapter = new TestSelectableAdapter(mItems); + recyclerView.setAdapter(mAdapter); + + RadioGroup rgMode = findViewById(R.id.rgMode); + rgMode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + switch (checkedId) { + case R.id.single: + mAdapter.setSelectMode(SelectableHelper.SelectMode.SINGLE); + break; + case R.id.multiple: + mAdapter.setSelectMode(SelectableHelper.SelectMode.MULTIPLE); + break; + } + } + }); + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.btnAdd: + // 在索引 2 处添加 5 个元素 + mItems.addAll(2, generateItems()); + mAdapter.setItems(mItems); + break; + case R.id.btnRemove: + // 从索引 0 处移除 5 个元素 + for (int i = 0; i < 5 && i < mItems.size(); i++) { + mItems.remove(0); + } + mAdapter.setItems(mItems); + break; + case R.id.btnMove: + // 交换索引为 0 和 5 的元素 + if (mItems.size() < 3) { + Toast.makeText(this, "ignore: item < 3", Toast.LENGTH_SHORT).show(); + return; + } + String item = mItems.get(1); + int targetIndex = Math.min(5, mItems.size() - 1); + mItems.set(1, mItems.get(targetIndex)); + mItems.set(targetIndex, item); + mAdapter.setItems(mItems); + break; + } + } + + // *****************************private********************************* + + private List generateItems() { + List items = new ArrayList<>(5); + + int end = mIndex + 5; + for (; mIndex < end; mIndex++) { + items.add("new item: " + mIndex); + } + + return items; + } + + public static class TestSelectableAdapter extends SelectableAdapter { + private List mItems; + + public TestSelectableAdapter(List items) { + mItems = new ArrayList<>(items); + + setOnItemClickListener(new ItemClickHelper.OnItemClickListener() { + @Override + public void onItemClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { + setSelect(position, !isSelected(position)); + } + }); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_selectable, parent, false); + return new ViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { + super.onBindViewHolder(holder, position); + + holder.tvTitle.setText(mItems.get(position)); + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + public void setItems(List items) { + DiffCallback diffCallback = new DiffCallback(mItems, items); + DiffUtil.DiffResult result = DiffUtil.calculateDiff(diffCallback, true); + result.dispatchUpdatesTo(this); + mItems = new ArrayList<>(items); + } + + public static class ViewHolder extends RecyclerView.ViewHolder implements SelectableHelper.Selectable { + public TextView tvTitle; + + private int mDefaultColor; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + + tvTitle = itemView.findViewById(R.id.tvTitle); + + mDefaultColor = tvTitle.getCurrentTextColor(); + } + + @Override + public void onSelected() { + tvTitle.setTextColor(Color.RED); + } + + @Override + public void onUnselected() { + tvTitle.setTextColor(mDefaultColor); + } + } + + private static class DiffCallback extends DiffUtil.Callback { + private List mOldList; + private List mNewList; + + DiffCallback(List oldList, List newList) { + mOldList = oldList; + mNewList = newList; + } + + @Override + public int getOldListSize() { + return mOldList.size(); + } + + @Override + public int getNewListSize() { + return mNewList.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + if (oldItemPosition > mOldList.size() + || newItemPosition > mNewList.size()) { + return false; + } + + return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition)); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition)); + } + } + } +} diff --git a/app/src/main/java/com/test/helper/TestAdapter.java b/app/src/main/java/com/test/helper/TestAdapter.java deleted file mode 100644 index 4594adb..0000000 --- a/app/src/main/java/com/test/helper/TestAdapter.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.test.helper; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import recyclerview.helper.ItemClickHelper; - -public class TestAdapter extends RecyclerView.Adapter { - private Context mContext; - private int mSize; - private ItemClickHelper mItemClickHelper; - - public TestAdapter(Context context, int size) { - mContext = context; - mSize = size; - mItemClickHelper = new ItemClickHelper(); - - mItemClickHelper.setOnItemClickListener(new ItemClickHelper.OnItemClickListener() { - @Override - public void onItemClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { - switch (viewId) { - case R.id.item: - Toast.makeText(mContext, "ItemClicked, position: " + position, Toast.LENGTH_SHORT).show(); - break; - case R.id.button: - Toast.makeText(mContext, "ButtonClicked, position: " + position, Toast.LENGTH_SHORT).show(); - break; - } - } - }); - - mItemClickHelper.setOnItemLongClickListener(new ItemClickHelper.OnItemLongClickListener() { - @Override - public boolean onItemLongClicked(int position, int viewId, View view, RecyclerView.ViewHolder holder) { - switch (viewId) { - case R.id.item: - Toast.makeText(mContext, "ItemLongClicked, position: " + position, Toast.LENGTH_SHORT).show(); - return true; - case R.id.button: - Toast.makeText(mContext, "ButtonLongClicked, position: " + position, Toast.LENGTH_SHORT).show(); - return true; - } - - return false; - } - }); - } - - @Override - public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - mItemClickHelper.attachToRecyclerView(recyclerView); - } - - @Override - public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { - super.onDetachedFromRecyclerView(recyclerView); - mItemClickHelper.detach(); - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_recycler, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - mItemClickHelper.bindClickListener(holder.itemView, holder.button); - mItemClickHelper.bindLongClickListener(holder.itemView, holder.button); - - holder.tvText.setText(String.valueOf(position)); - } - - @Override - public int getItemCount() { - return mSize; - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvText; - Button button; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - - tvText = itemView.findViewById(R.id.tvText); - button = itemView.findViewById(R.id.button); - } - } -} diff --git a/app/src/main/res/layout/activity_item_click_helper.xml b/app/src/main/res/layout/activity_item_click_helper.xml new file mode 100644 index 0000000..6e75f36 --- /dev/null +++ b/app/src/main/res/layout/activity_item_click_helper.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a6435c4..0c465d4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,4 +4,4 @@ android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity" /> \ No newline at end of file + tools:context="com.test.MainActivity" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scroll_to_position_helper.xml b/app/src/main/res/layout/activity_scroll_to_position_helper.xml new file mode 100644 index 0000000..30d729c --- /dev/null +++ b/app/src/main/res/layout/activity_scroll_to_position_helper.xml @@ -0,0 +1,38 @@ + + + + + + + +