Kotlin migration Featureadapter, ItemClickSupport (#1236)
This commit is contained in:
@@ -33,7 +33,7 @@ import java.util.*
|
||||
*
|
||||
*/
|
||||
class FeatureOverviewActivity : AppCompatActivity() {
|
||||
private var recyclerView: RecyclerView? = null
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private var sectionAdapter: FeatureSectionAdapter? = null
|
||||
private var features: List<Feature>? = null
|
||||
|
||||
@@ -42,17 +42,20 @@ class FeatureOverviewActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_feature_overview)
|
||||
recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
|
||||
recyclerView!!.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView!!.addOnItemTouchListener(SimpleOnItemTouchListener())
|
||||
recyclerView!!.setHasFixedSize(true)
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.addOnItemTouchListener(SimpleOnItemTouchListener())
|
||||
recyclerView.setHasFixedSize(true)
|
||||
|
||||
ItemClickSupport.addTo(recyclerView)
|
||||
.setOnItemClickListener { recyclerView: RecyclerView?, position: Int, view: View? ->
|
||||
if (!sectionAdapter!!.isSectionHeaderPosition(position)) {
|
||||
val itemPosition = sectionAdapter!!.getConvertedPosition(position)
|
||||
val feature = features!![itemPosition]
|
||||
startFeature(feature)
|
||||
.setOnItemClickListener(object : ItemClickSupport.OnItemClickListener {
|
||||
override fun onItemClicked(recyclerView: RecyclerView?, position: Int, view: View?) {
|
||||
if (sectionAdapter!!.isSectionHeaderPosition(position).not()) {
|
||||
val itemPosition = sectionAdapter!!.getConvertedPosition(position)
|
||||
val feature = features!![itemPosition]
|
||||
startFeature(feature)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (savedInstanceState == null) {
|
||||
loadFeatures()
|
||||
} else {
|
||||
@@ -164,7 +167,6 @@ class FeatureOverviewActivity : AppCompatActivity() {
|
||||
onFeaturesLoaded(features)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_STATE_FEATURES = "featureList"
|
||||
}
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
package org.maplibre.android.testapp.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.maplibre.android.testapp.utils.FontCache;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FeatureSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private static final int SECTION_TYPE = 0;
|
||||
|
||||
private final Context context;
|
||||
private final SparseArray<Section> sections;
|
||||
private final RecyclerView.Adapter adapter;
|
||||
|
||||
@LayoutRes
|
||||
private final int sectionRes;
|
||||
|
||||
@IdRes
|
||||
private final int textRes;
|
||||
|
||||
private boolean valid = true;
|
||||
|
||||
public FeatureSectionAdapter(Context ctx, int sectionResourceId, int textResourceId,
|
||||
RecyclerView.Adapter baseAdapter) {
|
||||
context = ctx;
|
||||
sectionRes = sectionResourceId;
|
||||
textRes = textResourceId;
|
||||
adapter = baseAdapter;
|
||||
sections = new SparseArray<>();
|
||||
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
valid = adapter.getItemCount() > 0;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeChanged(int positionStart, int itemCount) {
|
||||
valid = adapter.getItemCount() > 0;
|
||||
notifyItemRangeChanged(positionStart, itemCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
valid = adapter.getItemCount() > 0;
|
||||
notifyItemRangeInserted(positionStart, itemCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
valid = adapter.getItemCount() > 0;
|
||||
notifyItemRangeRemoved(positionStart, itemCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static class SectionViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView title;
|
||||
|
||||
public SectionViewHolder(@NonNull View view, @IdRes int textRes) {
|
||||
super(view);
|
||||
title = (TextView) view.findViewById(textRes);
|
||||
title.setTypeface(FontCache.get("Roboto-Medium.ttf", view.getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
|
||||
if (typeView == SECTION_TYPE) {
|
||||
final View view = LayoutInflater.from(context).inflate(sectionRes, parent, false);
|
||||
return new SectionViewHolder(view, textRes);
|
||||
} else {
|
||||
return adapter.onCreateViewHolder(parent, typeView - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
|
||||
if (isSectionHeaderPosition(position)) {
|
||||
String cleanTitle = sections.get(position).title.toString().replace("_", " ");
|
||||
((SectionViewHolder) sectionViewHolder).title.setText(cleanTitle);
|
||||
} else {
|
||||
adapter.onBindViewHolder(sectionViewHolder, getConvertedPosition(position));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return isSectionHeaderPosition(position)
|
||||
? SECTION_TYPE
|
||||
: adapter.getItemViewType(getConvertedPosition(position)) + 1;
|
||||
}
|
||||
|
||||
|
||||
public static class Section {
|
||||
int firstPosition;
|
||||
int sectionedPosition;
|
||||
CharSequence title;
|
||||
|
||||
public Section(int firstPosition, CharSequence title) {
|
||||
this.firstPosition = firstPosition;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setSections(Section[] sections) {
|
||||
this.sections.clear();
|
||||
|
||||
Arrays.sort(sections, (section, section1) -> (section.firstPosition == section1.firstPosition)
|
||||
? 0
|
||||
: ((section.firstPosition < section1.firstPosition) ? -1 : 1));
|
||||
|
||||
int offset = 0;
|
||||
for (Section section : sections) {
|
||||
section.sectionedPosition = section.firstPosition + offset;
|
||||
this.sections.append(section.sectionedPosition, section);
|
||||
++offset;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getConvertedPosition(int sectionedPosition) {
|
||||
if (isSectionHeaderPosition(sectionedPosition)) {
|
||||
return RecyclerView.NO_POSITION;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < sections.size(); i++) {
|
||||
if (sections.valueAt(i).sectionedPosition > sectionedPosition) {
|
||||
break;
|
||||
}
|
||||
--offset;
|
||||
}
|
||||
return sectionedPosition + offset;
|
||||
}
|
||||
|
||||
public boolean isSectionHeaderPosition(int position) {
|
||||
return sections.get(position) != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return isSectionHeaderPosition(position)
|
||||
? Integer.MAX_VALUE - sections.indexOfKey(position)
|
||||
: adapter.getItemId(getConvertedPosition(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return (valid ? adapter.getItemCount() + sections.size() : 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.maplibre.android.testapp.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.maplibre.android.testapp.utils.FontCache
|
||||
import java.util.*
|
||||
|
||||
class FeatureSectionAdapter(
|
||||
private val context: Context,
|
||||
@param:LayoutRes private val sectionRes: Int,
|
||||
@param:IdRes private val textRes: Int,
|
||||
private val adapter: FeatureAdapter
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private val sections = SparseArray<Section>()
|
||||
private var valid = true
|
||||
|
||||
private val adapterDataObserver: RecyclerView.AdapterDataObserver =
|
||||
object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
valid = adapter.itemCount > 0
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
|
||||
valid = adapter.itemCount > 0
|
||||
notifyItemRangeChanged(positionStart, itemCount)
|
||||
}
|
||||
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
valid = adapter.itemCount > 0
|
||||
notifyItemRangeInserted(positionStart, itemCount)
|
||||
}
|
||||
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
valid = adapter.itemCount > 0
|
||||
notifyItemRangeRemoved(positionStart, itemCount)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
adapter.registerAdapterDataObserver(adapterDataObserver)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, typeView: Int): RecyclerView.ViewHolder {
|
||||
return if (typeView == SECTION_TYPE) {
|
||||
val view = LayoutInflater.from(context).inflate(sectionRes, parent, false)
|
||||
SectionViewHolder(view, textRes)
|
||||
} else {
|
||||
adapter.onCreateViewHolder(parent, typeView - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(sectionViewHolder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (isSectionHeaderPosition(position)) {
|
||||
val cleanTitle = sections.get(position).title.toString().replace("_", " ")
|
||||
(sectionViewHolder as SectionViewHolder).title.text = cleanTitle
|
||||
} else {
|
||||
adapter.onBindViewHolder(sectionViewHolder as FeatureAdapter.ViewHolder, getConvertedPosition(position))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (isSectionHeaderPosition(position)) {
|
||||
SECTION_TYPE
|
||||
} else {
|
||||
adapter.getItemViewType(getConvertedPosition(position)) + 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return if (isSectionHeaderPosition(position)) {
|
||||
Integer.MAX_VALUE - sections.indexOfKey(position).toLong()
|
||||
} else {
|
||||
adapter.getItemId(getConvertedPosition(position))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (valid) adapter.itemCount + sections.size() else 0
|
||||
}
|
||||
|
||||
fun setSections(sections: Array<Section>) {
|
||||
this.sections.clear()
|
||||
Arrays.sort(sections) { section, section1 ->
|
||||
if (section.firstPosition == section1.firstPosition) {
|
||||
0
|
||||
} else if (section.firstPosition < section1.firstPosition) {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
for ((offset, section) in sections.withIndex()) {
|
||||
section.sectionedPosition = section.firstPosition + offset
|
||||
this.sections.append(section.sectionedPosition, section)
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getConvertedPosition(sectionedPosition: Int): Int {
|
||||
if (isSectionHeaderPosition(sectionedPosition)) {
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
var offset = 0
|
||||
for (i in 0 until sections.size()) {
|
||||
if (sections.valueAt(i).sectionedPosition > sectionedPosition) {
|
||||
break
|
||||
}
|
||||
--offset
|
||||
}
|
||||
return sectionedPosition + offset
|
||||
}
|
||||
|
||||
fun isSectionHeaderPosition(position: Int): Boolean {
|
||||
return sections.get(position) != null
|
||||
}
|
||||
|
||||
class SectionViewHolder(view: View, textRes: Int) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
val title: TextView = view.findViewById(textRes)
|
||||
|
||||
init {
|
||||
title.typeface = FontCache["Roboto-Medium.ttf", view.context]
|
||||
}
|
||||
}
|
||||
|
||||
class Section(val firstPosition: Int, val title: CharSequence) {
|
||||
var sectionedPosition = 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SECTION_TYPE = 0
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package org.maplibre.android.testapp.utils;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.maplibre.android.testapp.R;
|
||||
|
||||
public class ItemClickSupport {
|
||||
private final RecyclerView recyclerView;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
private OnItemLongClickListener onItemLongClickListener;
|
||||
private View.OnClickListener onClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (onItemClickListener != null) {
|
||||
RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(view);
|
||||
onItemClickListener.onItemClicked(recyclerView, holder.getAdapterPosition(), view);
|
||||
}
|
||||
}
|
||||
};
|
||||
private View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (onItemLongClickListener != null) {
|
||||
RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(view);
|
||||
return onItemLongClickListener.onItemLongClicked(recyclerView, holder.getAdapterPosition(), view);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
private RecyclerView.OnChildAttachStateChangeListener attachListener
|
||||
= new RecyclerView.OnChildAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onChildViewAttachedToWindow(View view) {
|
||||
if (onItemClickListener != null) {
|
||||
view.setOnClickListener(onClickListener);
|
||||
}
|
||||
if (onItemLongClickListener != null) {
|
||||
view.setOnLongClickListener(onLongClickListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildViewDetachedFromWindow(View view) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private ItemClickSupport(RecyclerView recyclerView) {
|
||||
this.recyclerView = recyclerView;
|
||||
this.recyclerView.setTag(R.id.item_click_support, this);
|
||||
this.recyclerView.addOnChildAttachStateChangeListener(attachListener);
|
||||
}
|
||||
|
||||
public static ItemClickSupport addTo(RecyclerView view) {
|
||||
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
|
||||
if (support == null) {
|
||||
support = new ItemClickSupport(view);
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
public static ItemClickSupport removeFrom(RecyclerView view) {
|
||||
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
|
||||
if (support != null) {
|
||||
support.detach(view);
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
|
||||
onItemClickListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
|
||||
onItemLongClickListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void detach(RecyclerView view) {
|
||||
view.removeOnChildAttachStateChangeListener(attachListener);
|
||||
view.setTag(R.id.item_click_support, null);
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
|
||||
void onItemClicked(RecyclerView recyclerView, int position, View view);
|
||||
}
|
||||
|
||||
public interface OnItemLongClickListener {
|
||||
|
||||
boolean onItemLongClicked(RecyclerView recyclerView, int position, View view);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.maplibre.android.testapp.utils
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.maplibre.android.testapp.R
|
||||
|
||||
class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {
|
||||
private var onItemClickListener: OnItemClickListener? = null
|
||||
private var onItemLongClickListener: OnItemLongClickListener? = null
|
||||
private val onClickListener = View.OnClickListener { view ->
|
||||
onItemClickListener?.let {
|
||||
val holder = recyclerView.getChildViewHolder(view)
|
||||
it.onItemClicked(recyclerView, holder.adapterPosition, view)
|
||||
}
|
||||
}
|
||||
private val onLongClickListener = View.OnLongClickListener { view ->
|
||||
onItemLongClickListener?.let {
|
||||
val holder = recyclerView.getChildViewHolder(view)
|
||||
it.onItemLongClicked(recyclerView, holder.adapterPosition, view)
|
||||
}
|
||||
false
|
||||
}
|
||||
private val attachListener = object : RecyclerView.OnChildAttachStateChangeListener {
|
||||
override fun onChildViewAttachedToWindow(view: View) {
|
||||
onItemClickListener?.let {
|
||||
view.setOnClickListener(onClickListener)
|
||||
}
|
||||
onItemLongClickListener?.let {
|
||||
view.setOnLongClickListener(onLongClickListener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChildViewDetachedFromWindow(view: View) {}
|
||||
}
|
||||
|
||||
fun setOnItemClickListener(listener: OnItemClickListener?): ItemClickSupport {
|
||||
onItemClickListener = listener
|
||||
return this
|
||||
}
|
||||
|
||||
fun setOnItemLongClickListener(listener: OnItemLongClickListener?): ItemClickSupport {
|
||||
onItemLongClickListener = listener
|
||||
return this
|
||||
}
|
||||
|
||||
private fun detach(view: RecyclerView) {
|
||||
view.removeOnChildAttachStateChangeListener(attachListener)
|
||||
view.setTag(R.id.item_click_support, null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun addTo(view: RecyclerView): ItemClickSupport {
|
||||
var support = view.getTag(R.id.item_click_support) as ItemClickSupport?
|
||||
if (support == null) {
|
||||
support = ItemClickSupport(view)
|
||||
view.setTag(R.id.item_click_support, support)
|
||||
view.addOnChildAttachStateChangeListener(support.attachListener)
|
||||
}
|
||||
return support
|
||||
}
|
||||
|
||||
fun removeFrom(view: RecyclerView): ItemClickSupport? {
|
||||
val support = view.getTag(R.id.item_click_support) as ItemClickSupport?
|
||||
support?.let {
|
||||
it.detach(view)
|
||||
}
|
||||
return support
|
||||
}
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(recyclerView: RecyclerView?, position: Int, view: View?)
|
||||
}
|
||||
|
||||
interface OnItemLongClickListener {
|
||||
fun onItemLongClicked(recyclerView: RecyclerView?, position: Int, view: View?): Boolean
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user