How can I set selected item background in MvxRecycleView?
I want to change background of selected item while another.
selector_category_item.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:state_enabled="true" >
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary"/>
</shape>
</item>
<item android:state_checked="false" android:state_enabled="true" >
<shape android:shape="rectangle">
<solid android:color="#color/grayBackground"/>
</shape>
</item>
</selector>
My MvxRecycleView
<MvxRecyclerView
android:overScrollMode="never"
android:scrollbars="vertical"
android:background="#e2e2e2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
local:MvxItemTemplate="#layout/category_item"
local:MvxBind="ItemsSource Categories; ItemClick SelectCategoryCommand; SelectedItem SelectedCategory"
android:id="#+id/categoryRecyclerView" />
And category_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:clickable="true"
android:background="#drawable/selector_category_item"
android:layout_marginBottom="1dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ffimageloading.cross.MvxCachedImageView
android:id="#+id/categoryImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="DrawableName CategoryImage" />
<ImageView
android:id="#+id/favIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:src="#drawable/no_fav_icon" />
</RelativeLayout>
<TextView
android:clickable="true"
android:focusableInTouchMode="true"
android:focusable="true"
android:layout_marginTop="8dp"
android:id="#+id/categoryText"
style="#style/TextStyleBlack"
android:fontFamily="#font/roboto_regular"
android:letterSpacing="-0.04"
android:lineSpacingExtra="0sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="Text Name"
android:textSize="14sp" />
</LinearLayout>
I need change selected item background to other while item is selected.
How can I sort it out?
You can make a custom MvxRecyclerView adapter to handle selection:
public class MyRecyclerAdapter : MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerAdapter
{
public MyRecyclerAdapter(IMvxAndroidBindingContext bindingContext) : base(bindingContext)
{
}
public MyRecyclerAdapter(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
base.OnBindViewHolder(holder, position);
holder.ItemView.Selected = _selectedPosition == position;
holder.ItemView.Click += (s, e) => SelectIndex(holder.AdapterPosition);
}
private void SelectIndex(int index)
{
NotifyItemChanged(_selectedPosition);
_selectedPosition = index;
NotifyItemChanged(_selectedPosition);
}
private int _selectedPosition = RecyclerView.NoPosition;
}
Then you want to set the MvxRecyclerView.Adapter property somewhere in your view. The ideal place is usually OnCreate for an activity, or OnCreateView for a fragment:
recyclerView.Adapter = new MyRecyclerAdapter((IMvxAndroidBindingContext)BindingContext);
At this point you have a RecyclerView which sets the item you click on as selected. Now you can use a ColorStateList to change the color depending on the selection state. Add the file to Resources\drawable\selector_category_item.xml:
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#color/colorAccent"
android:state_pressed="true" />
<item
android:drawable="#color/colorPrimary"
android:state_selected="true" />
</selector>
Finally set the background of the category_item.xml LinearLayout to:
android:background="#drawable/selector_category_item"
It's worth pointing out you can also use a different ColorStateList to change the android:textColor as desired based on the selection state as well. You just need to put those ColorStateList XML files in the Resources\color folder instead of Resources\drawable.
I posted a working sample on GitHub.
Make boolean flag to save selected state for each category item on Category model.
On your adapter viewHolder make instance for recycler item root view mBackground.
On onBindViewHolder update mBackground selection status.
class MyDataHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
LinearLayout mBackground;
MyDataHolder(View itemView) {
super(itemView);
mBackground = (LinearLayout) itemView.findViewById(R.id.my_item_root_view);
itemView.setOnClickListener(this);
}
#Override
public void onBindViewHolder(MyCategoryAdapter.MyDataHolder holder, int position) {
final Category category = getItem(position);
holder.mBackground.setSelected(category.isSelected());
}
Related
Probably many of you (as me), have problem with creating ViewPager with bottom dots, like this:
How do you create such an Android ViewPager?
All we need are: ViewPager, TabLayout and 2 drawables for selected and default dots.
Firstly, we have to add TabLayout to our screen layout, and connect it with ViewPager. We can do this in two ways:
Nested TabLayout in ViewPager
<androidx.viewpager.widget.ViewPager
android:id="#+id/photos_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</androidx.viewpager.widget.ViewPager>
In this case TabLayout will be automatically connected with ViewPager, but TabLayout will be next to ViewPager, not over it.
Separate TabLayout
<androidx.viewpager.widget.ViewPager
android:id="#+id/photos_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
In this case, we can put TabLayout anywhere, but we have to connect TabLayout with ViewPager programmatically
ViewPager pager = (ViewPager) view.findViewById(R.id.photos_viewpager);
PagerAdapter adapter = new PhotosAdapter(getChildFragmentManager(), photosUrl);
pager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(pager, true);
Once we created our layout, we have to prepare our dots. So we create three files: selected_dot.xml, default_dot.xml and tab_selector.xml.
selected_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="#color/colorAccent"/>
</shape>
</item>
</layer-list>
default_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="#android:color/darker_gray"/>
</shape>
</item>
</layer-list>
tab_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/selected_dot"
android:state_selected="true"/>
<item android:drawable="#drawable/default_dot"/>
</selector>
Now we need to add only 3 lines of code to TabLayout in our XML layout.
app:tabBackground="#drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
First Create a layout, in that give one LinerLayout for Dots which show over your View Pager
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="#+id/pager_dots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:background="#android:color/transparent"
android:gravity="center_horizontal"
android:orientation="horizontal">
</LinearLayout>
</RelativeLayout>
After that create 2 drawables
1. Unselected Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#android:color/transparent"/>
<size android:width="12dp" android:height="12dp"/>
<stroke android:width="1dp" android:color="#ffffff"/>
</shape>
2. Selected Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#android:color/transparent"/>
<size android:width="12dp" android:height="12dp"/>
<stroke android:width="1dp" android:color="#000000"/>
</shape>
After that set adapter
private LinearLayout llPagerDots;
private ViewPager viewPager;
private ArrayList<String> eventImagesUrl;
private HomeViewPagerAdapter homeViewPagerAdapter;
private ImageView[] ivArrayDotsPager;
public void setUpViewPager() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
llPagerDots = (LinearLayout) findViewById(R.id.pager_dots);
homeViewPagerAdapter = new HomeViewPagerAdapter(mContext, eventImagesUrl);
viewPager.setAdapter(homeViewPagerAdapter);
setupPagerIndidcatorDots();
ivArrayDotsPager[0].setImageResource(R.drawable.page_indicator_selected);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
for (int i = 0; i < ivArrayDotsPager.length; i++) {
ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
}
ivArrayDotsPager[position].setImageResource(R.drawable.page_indicator_selected);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
Create a method setupPagerIndidcatorDots() :
private void setupPagerIndidcatorDots() {
ivArrayDotsPager = new ImageView[eventImagesUrl.size()];
for (int i = 0; i < ivArrayDotsPager.length; i++) {
ivArrayDotsPager[i] = new ImageView(getActivity());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
ivArrayDotsPager[i].setLayoutParams(params);
ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
//ivArrayDotsPager[i].setAlpha(0.4f);
ivArrayDotsPager[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
view.setAlpha(1);
}
});
llPagerDots.addView(ivArrayDotsPager[i]);
llPagerDots.bringToFront();
}
You can check out my library to handle your request : https://github.com/tommybuonomo/dotsindicator
In your XML layout
<com.tbuonomo.viewpagerdotsindicator.DotsIndicator
android:id="#+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
app:dotsColor="#color/colorPrimary"
app:dotsSize="16dp"
app:dotsWidthFactor="3"
/>
In your Java code
dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
dotsIndicator.setViewPager(viewPager);
When you want something similar to this with the latest ViewPager2 and Kotlin
Everything is self-explaining, no need to explain!
1. Your Activity or Fragment
val imageList = listOf(
ImageModel(R.drawable.offer1),
ImageModel(R.drawable.splash),
ImageModel(R.drawable.offer1),
ImageModel(R.drawable.splash2)
)
val adapter = HomeOffersAdapter()
adapter.setItem(imageList)
photos_viewpager.adapter = adapter
TabLayoutMediator(tab_layout, photos_viewpager) { tab, position ->
}.attach()
}
2. Layout
<RelativeLayout 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="#dimen/dp_200">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/photos_viewpager"
android:layout_width="match_parent"
android:layout_height="#dimen/dp_200" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center"
app:tabBackground="#drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="#android:color/transparent"
app:tabTextColor="#android:color/transparent" />
3. Drawable: tab_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/dot_selected" android:state_selected="true" />
<item android:drawable="#drawable/dot_default" />
4. Drawable: dot_selected.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="#dimen/dp_8"
android:useLevel="false">
<solid android:color="#color/colorPrimary" />
<stroke
android:width="#dimen/dp_1"
android:color="#android:color/white" />
5. Drawable: dot_default.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="#dimen/dp_8"
android:useLevel="false">
<solid android:color="#android:color/transparent" />
<stroke
android:width="#dimen/dp_1"
android:color="#android:color/white" />
6. Adapter
class HomeOffersAdapter : RecyclerView.Adapter<HomeOffersAdapter.HomeOffersViewHolder>() {
private var list: List<ImageModel> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOffersViewHolder {
return HomeOffersViewHolder(parent)
}
override fun onBindViewHolder(holder: HomeOffersViewHolder, position: Int) {
holder.bind(list[position])
}
fun setItem(list: List<ImageModel>) {
this.list = list
notifyDataSetChanged()
}
override fun getItemCount(): Int = list.size
class HomeOffersViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
constructor(parent: ViewGroup) : this(
LayoutInflater.from(parent.context).inflate(
R.layout.pager_item,
parent, false
)
)
fun bind(imageModel: ImageModel) {
itemView.offerImage.setImageResource(imageModel.image)
}
}
}
7. Layout: pager_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/offerImage"
android:layout_width="match_parent"
android:layout_height="#dimen/dp_200"
android:adjustViewBounds="true"
android:scaleType="fitXY"
tools:src="#drawable/offer1" />
2021, how to actually do it. ViewPager2 only.
Refer to this excellent short article, which has a couple of problems:
https://medium.com/#adrian.kuta93/android-viewpager-with-dots-indicator-a34c91e59e3a
Starting with a normal Android Studio default project as of 2021, with a reasonably new minimum (24 currently)...
General concept:
Make a standard TabLayout, but replace each "tab unit" with "a little dot" rather than the usual text.
In TabLayout, you can indeed replace each "tab unit" using "tabBackground":
app:tabBackground="#drawable/tab_selector"
So add the following to the XML of your screen, which has a ViewPager2:
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="#00FFFFFF"
app:tabBackground="#drawable/tab_selector"
app:tabIndicatorGravity="center"
app:tabIndicatorHeight="0dp"/>
Notice carefully we are replacing each and every one of the "tab units" in the TabLayout, with our own "tab_selector".
To be completely clear, "tabBackground" means the individual little "tab units", not the whole tab bar system.
(Aside, note that the two tricks tabIndicatorGravity and tabIndicatorHeight will indeed get rid of the "boxes" that are the usual "tab units".)
Next create three drawables in the obvious way, tab_selector and the two different dots. See the above article or 100s of examples on this page.
The magic code:
In your onCreate have the expected code ...
viewPager = findViewById(R.id.simple_slide_pager);
tab_layout = findViewById(R.id.tab_layout);
viewPager.setAdapter(new ScreenSlidePagerAdapter(this));
and then here at last is the magic code fragment to make it work. Follow the above by:
Up to date for 2021:
TabLayoutMediator tabLayoutMediator =
new TabLayoutMediator(tab_layout, viewPager, true,
new TabLayoutMediator.TabConfigurationStrategy() {
#Override public void onConfigureTab(
#NonNull TabLayout.Tab tab, int position) { }
}
);
tabLayoutMediator.attach();
It's done.
(Inside onConfigureTab you can do any sound effects or whatever the heck might be needed. For shorter syntax see the key comment by #F1iX above.)
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="#+id/vpImage"
android:layout_width="match_parent"
android:layout_height="#dimen/_150sdp" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tlImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="#drawable/selector_product_image"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="12dp"
app:tabRippleColor="#null" />
</androidx.appcompat.widget.LinearLayoutCompat>
ImageAdapter imageAdapter = new ImageAdapter(getActivity(), arrayListSlider);
binding.vpImage.setOffscreenPageLimit(1);
binding.vpImage.setAdapter(imageAdapter);
binding.tlImage.setupWithViewPager(binding.vpImage);
selector_product_image.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/image_selected" android:state_selected="true" />
<item android:drawable="#drawable/image_unselected" />
</selector>
image_selected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="#color/colorAccent" />
</shape>
</item>
</layer-list>
image_unselected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="#color/colorPrimary" />
</shape>
</item>
</layer-list>
ImageAdapter.java
class ImageAdapter extends PagerAdapter {
private Context context;
private ArrayList<ImageModel> arrayList;
private LayoutInflater layoutInflater;
public ImageAdapter(Context context, ArrayList<ImageModel> arrayList) {
this.context = context;
this.arrayList = arrayList;
this.layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object o) {
return view == ((View) o);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View view = layoutInflater.inflate(R.layout.row_slider_image, container, false);
AppCompatImageView ivProductImage = view.findViewById(R.id.ivProductImage);
if (!TextUtils.isEmpty(arrayList.get(position).getImage())) {
Glide.with(context)
.load(arrayList.get(position).getImage())
.apply(new RequestOptions().placeholder(R.drawable.no_image).error(R.drawable.no_image))
.into(ivProductImage);
}
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
row_slider_image.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/ivProductImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="#drawable/no_image" />
</androidx.appcompat.widget.LinearLayoutCompat>
For Viewpager2, follow the same steps which suggested by #RediOne1 and in the activity or fragment use below code to attach tablayout with viwpager2
val tabLayoutMediator = TabLayoutMediator(binding.tabLayout,binding.offersVp) { _, _ -> }
tabLayoutMediator.attach()
Your xml
<RelativeLayout
android:id="#+id/rl_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/ll_dashboard_buttons"
android:layout_below="#+id/ib_menu">
<com.smart.gps.speedometer.app.utils.SmartViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
</com.smart.gps.speedometer.app.utils.SmartViewPager>
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="#drawable/tab_selector"
app:tabIndicatorHeight="0dp"
app:tabGravity="center"
/>
create a drawable. right click on drawable -> new -> Drawable file resource
name that file
tab_selector.xml
<item android:drawable="#drawable/selected_tab"
android:state_selected="true"/>
<item android:drawable="#drawable/unselected_tab"/>
Now there is two more xml files. create two more xml files with respected name. these are the selecter indicator and unselected indicator
selected_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="#color/highspeed"/>
</shape>
</item>
</layer-list>
unselected_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="#android:color/darker_gray"/>
</shape>
</item>
</layer-list>
Place ViewFlipper and viewFlipper_linear_dot_lay(Linearlayout) on samebaseline and follow the below one
viewFlipper_linear_dot_lay= (LinearLayout) findViewById(R.id.dots_lay);
setupDotsOnViewPager(images_viewFlipper);
for (int i = 0; i < images_viewFlipper.size(); i++) {
//Add Images to ViewFlipper
}
private void setupDotsOnViewPager(ArrayList images_viewFlipper) {
images_linear = new ImageView[images_viewFlipper.size()];
for (int i = 0; i < images_linear.length; i++) {
images_linear[i] = new ImageView(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
params.gravity = Gravity.BOTTOM | Gravity.CENTER;
images_linear[i].setLayoutParams(params);
images_linear[i].setImageResource(R.drawable.unselected);
viewFlipper_linear_dot_lay.addView(images_linear[i]);
viewFlipper_linear_dot_lay.bringToFront();
}
}
And OnRight & OnLeft getsures place the below code
for (int i = 0; i < images_linear.length; i++) {
images_linear[i].setImageResource(R.drawable.unselected);
}
images_linear[viewFlipper.getDisplayedChild()].setImageResource(R.drawable.selected);
Add dependencies > Sync the Gradle
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2'
In your java code
dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator3);
myViewPagerAdapter = new MyViewPagerAdapter();
viewPager.setAdapter(myViewPagerAdapter);
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
dotsIndicator.setViewPager(viewPager);
In your layout
<com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
android:id="#+id/spring_dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:dampingRatio="0.5"
app:dotsColor="#color/material_white"
app:dotsStrokeColor="#color/material_yellow"
app:dotsCornerRadius="2dp"
app:dotsSize="16dp"
app:dotsSpacing="6dp"
app:dotsStrokeWidth="2dp"
app:stiffness="300"
/>
I pretty like the selector behavior generated in navigation drawer.
It has ripple effect.
Its ImageView and TextView has proper color, when being selected.
In my dialog, I try to achieve the same effect, by using the following layout.
label_array_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?android:attr/selectableItemBackground"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<ImageView
android:duplicateParentState="true"
android:id="#+id/image_view"
android:paddingStart="24dp"
android:paddingLeft="24dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="?attr/labelIconSelector" />
<TextView
android:duplicateParentState="true"
android:textSize="16sp"
android:id="#+id/text_view"
android:layout_width="0dp"
android:width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="?attr/labelTextViewColorSelector" />
</LinearLayout>
labelIconSelector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<bitmap android:src="#drawable/ic_label_white_24dp"
android:tint="#color/colorPrimaryBrown" />
</item>
<item>
<bitmap android:src="#drawable/ic_label_white_24dp"
android:tint="#ff757575" />
</item>
</selector>
labelTextViewColorSelector
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="#color/colorPrimaryBrown" />
<item android:color="#color/primaryTextColorLight" />
</selector>
In my ArrayAdapter, I try to programmatically select the 1st item via view.setSelected(true)
public class LabelArrayAdapter extends ArrayAdapter<TabInfo> {
private static class ViewHolder {
public final ImageView imageView;
public final TextView textView;
public ViewHolder(View view) {
imageView = view.findViewById(R.id.image_view);
textView = view.findViewById(R.id.text_view);
Utils.setCustomTypeFace(textView, Utils.ROBOTO_REGULAR_TYPE_FACE);
}
}
public LabelArrayAdapter(#NonNull Context context, List<TabInfo> tabInfos) {
super(context, R.layout.label_array_adapter, tabInfos);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflator = LayoutInflater.from(this.getContext());
view = inflator.inflate(R.layout.label_array_adapter, null);
final ViewHolder viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
// Not sure why this is required.
view.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, Utils.dpToPixel(48)));
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
TabInfo tabInfo = getItem(position);
if (tabInfo == null) {
holder.imageView.setVisibility(View.INVISIBLE);
holder.textView.setText("(No label)");
} else {
holder.imageView.setVisibility(View.VISIBLE);
holder.textView.setText(tabInfo.getName());
}
if (position == 0) {
view.setSelected(true);
} else {
view.setSelected(false);
}
return view;
}
}
This is how I try to show Dialog via DialogFragment.
return new AlertDialog.Builder(getActivity())
.setTitle("Move to")
.setAdapter(new LabelArrayAdapter(this.getContext(), customTabInfos), (dialog, which) -> {
})
.create();
However, it doesn't work as you can see in the following screenshot. The 1st item doesn't look like it is being selected.
May I know, is there anything I had missed out?
How to make a View looks like it is being selected programmatically, if its background is ?android:attr/selectableItemBackground (ListView in AlertDialog)
Update
Using
alertDialog.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
alertDialog.getListView().setSelection(position);
wouldn't help either.
Here is an approach using a layer list drawable as the background for the view group that represents the item being selected.
First, for API 21+, we define a layer list drawable that displays the selected color when the item is selected. A call to View#setSelected() must be made for this to have an effect.
layer_list.xml (v21)
color/selected is #2000ffe5 for the demos.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector>
<item
android:drawable="#color/selected"
android:state_selected="true" />
</selector>
</item>
<item
android:id="#+id/selectableBackground"
android:drawable="?android:selectableItemBackground" />
</layer-list>
For API < 21, we cannot use android:drawable="?android:selectableItemBackground" in the layer list, so we will fall back to an alternate but similar visual cue.
layer_list < API 21
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector android:exitFadeDuration="#android:integer/config_shortAnimTime">
<item
android:drawable="#color/selected"
android:state_pressed="false"
android:state_selected="true" />
<item
android:drawable="#color/selectable_background"
android:state_pressed="true" />
</selector>
</item>
</layer-list>
I am selecting multiple item in listview for delete . I can delete multiple item . The code is as follows :
smsListView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
// Capture total checked items
final int checkedCount = smsListView.getCheckedItemCount();
// Set the CAB title according to total checked items
mode.setTitle(checkedCount + " Selected");
View v = smsListView.getChildAt(position
- smsListView.getFirstVisiblePosition());
// Calls toggleSelection method from ListViewAdapter Class
((SmsArrayAdapter) arrayAdapter).toggleSelection(position,v);
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.delete:
// Calls getSelectedIds method from ListViewAdapter Class
SparseBooleanArray selected = ((SmsArrayAdapter) arrayAdapter)
.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
SMSItem selecteditem = (SMSItem) arrayAdapter.getItem(selected.keyAt(i));
// Remove selected items following the ids
arrayAdapter.remove(selecteditem);
}
}
// Close CAB
mode.finish();
return true;
default:
return false;
}
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
((SmsArrayAdapter) arrayAdapter).removeSelection();
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
});
But I want to change color of the rows that I have selected . Currently there are no color on selection item in the list .
I have tried the following .
Step no 1: write below line to to your listview item layout
android:background="?android:attr/activatedBackgroundIndicator"
Step no 2: write below line to style.xml file
<style name="AppTheme" parent="#style/Theme.AppCompat.Light">
<item name="actionBarStyle">#style/MyActionBar</item>
<item name="android:activatedBackgroundIndicator">#drawable/muliple_selected_item</item>
</style>
Step no 3: Create muliple_selected_item.xml into Drawable folder and write below code.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:state_checked="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:state_pressed="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:drawable="#android:color/transparent" />
</selector>
But by this code , all itemsof the list are colored when I select any item of the listview . I want to change background color to those items only which I have selected .
How can I do this ?
The listview layoput is as follows :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/MainLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/smsItemContainerRelativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:layout_gravity="center_horizontal|top"
android:text="SMS Inbox"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/unread_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="-20dp"
android:background="#drawable/notification_bg"
android:gravity="center"
android:text="88"
android:textColor="#android:color/background_light"
android:textSize="20sp"
android:textStyle="bold"
android:layout_alignParentRight="true"
android:visibility="invisible" />
</RelativeLayout>
<ListView
android:id="#+id/SMSList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp" />
</LinearLayout>
The rows layout is as follows :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#drawable/muliple_selected_item" >>
<TextView
android:id="#+id/textView_from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SMS From"
android:textAppearance="?android:attr/textAppearanceLarge" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/textView_sms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="SMS : " />
<TextView
android:id="#+id/textView_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:text="Time : " />
</LinearLayout>
</LinearLayout>
The muliple_selected_item.xml is as follows : .
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:state_checked="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:state_pressed="true" android:drawable="#color/lvMultipleSelectionColor" />
<item android:drawable="#android:color/transparent" />
</selector>
You need to set Multiple Select Choice mode to your ListView
smsListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
You need to create Drawable file for your ListView's row Layout and use Drawable as background in that layout.
Here is Drawable file:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/menuitem_press_background_color" android:state_activated="true" />
<item android:drawable="#color/white" />
</selector>
EDIT:
remove changeBackgroundColor() used in selectView() and check if it works
You can refer this Selecting multiple items in ListView. it might help you
Just put the line below in your single row xml file(the xml file in which you define the define the layout for single row of listview):-
android:background="?android:attr/activatedBackgroundIndicator"
And no need to edit any more xml files. For more details, please visit the link below :-
Click here to go to link ...!
Hope this helps :)
You can use drawables for that like below,
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_activated="true"
android:drawable="#android:color/darker_gray" />
</selector>
and set it as background of your row layout,
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/list_item_background_activated" >
</RelativeLayout>
You can Achieve it in this way:-
First add property(variable) isSelected to SMSItem class with default Value false.
on onItemCheckedStateChanged method Execution set value for variable isSelected.
on getView method of Adapter you have to chech value for isSelected variable. then make a decison base on it, if false then set default
background and if true the set another background to perticular row.
call notifyDataSetChanged() method on Adepter from onItemCheckedStateChanged.
on Your Request, Im posting Code:-
add this to onItemCheckedStateChanged method
SMSItem selecteditem = (SMSItem) arrayAdapter.getItem(selected.keyAt(i));
selecteditem.setSelection(checked);
notifydatasetChanged();
and manage getView yourself
I have been making an application that works with ListViews in Android, and I can't make it so that the selected (chacked) item has a different background. I'm using CHOICE_MODE_SINGLE. This is how my code looks so far:
The ListView that I use:
(inside layout.xml)
<ListView
android:id="#+id/listView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:listSelector="#drawable/selector_test" >
</ListView>
The TextView layout I use in the adapter:
(listItem.xml)
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="23sp"
android:textStyle="bold" />
This is where I add the adapter:
mListAdapter = new ArrayAdapter<String>(this, R.layout.listItem, mList);
mListView = (ListView) findViewById(R.id.listView);
mListView.setAdapter(mAuthorAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
String selected = mList.get(position);
// ...
mListView.setItemChecked(position, true);
}
});
I'm sure that the proper item is checked on click, because when I call getCheckedItemPosition(), it returns the proper value.
And now, the two things I tried in order to highlight the checked item:
Selector drawable:
(selector_test.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="#android:integer/config_longAnimTime">
<item android:state_checked="true"><shape>
<solid android:color="#color/red" />
</shape></item>
<item android:state_selected="true"><shape>
<solid android:color="#color/blue" />
</shape></item>
<item android:state_pressed="true"><shape>
<solid android:color="#color/green" />
</shape></item>
</selector>
I add it to .xml with:
android:listSelector="#drawable/selector_test"
Background drawable:
(background_test.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/red" android:state_checked="true"/>
<item android:drawable="#color/blue" android:state_selected="true"/>
<item android:drawable="#color/green"/>
</selector>
I add it to .xml with:
android:background="#drawable/background_test"
I've tried adding the selector and the background to both listView.xml and listItem.xml, but the only thing that changes is the default background color, and the color of the selector when the item is pressed (or held).
android:state_checked="true" and android:state_selected="true" seem to do nothing.
I can change the background by overriding the getView() method in ArrayAdapter and invoking setBackgroundColor() in it if the view is selected, and it does change the background, but also gets rid of the selector entirely. Also, I don't really like to override classes just to change one line of code, especially if that same thing can be achieved in a different way.
So what I'm asking is, is there a way to highlight checked item in ListView by adding a selector or background drawable to it, and I am just doing it wrong, or will I have to make it work some other way.
Thanks in advance! :)
add this line in onStart of your activity
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
where lv is instance of listView
then override this method and add the following lines to it.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Set the item as checked to be highlighted
lv.setItemChecked(position, true);
v.setBackgroundColor(Color.BLUE);
conversationAdapter.notifyDataSetChanged();
}
and then change the color of previous selected item's background back to normal in getView method of your custom adapter
Try this:
listViewDay.setItemChecked(position, true);
listViewDay.setSelection(position);
listitem.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:background="#drawable/list_day_selector" >
<TextView
android:id="#+id/txtItemDay"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:text="22"
android:textColor="#android:color/white"
android:textSize="22sp" />
</LinearLayout>
list_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/list_item_bg_normal" android:state_activated="false"/>
<item android:drawable="#drawable/bg_with_left_arrow" android:state_pressed="true"/>
<item android:drawable="#drawable/bg_with_left_arrow" android:state_activated="true"/>
</selector>
Programmatically, use setSelector. For example:
lv.setSelector(R.color.abc_background_cache_hint_selector_material_dark);
After some hours of encountering one glitch after another (including the selector leaving a dirty stripe), I decided to do it myself:
class MyListAdapter extends ArrayAdapter<MyData> {
// !!! access to the current selection
int mSelected = 0;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
// !!! this is where the selected item is highlighted
v.findViewById(R.id.selectionSign).setVisibility(position == mSelected ? View.VISIBLE : View.INVISIBLE);
return v;
}
public CommandListAdapter(Context context, List<MyData> objects) {
super(context, R.layout.mylistitem, R.id.mytext, objects);
}
}
with the OnItemClickListener:
mMyListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
setSelectionPosNoUpd(position);
update();
}
});
and the functions defined as:
void setSelectionPosNoUpd(int n) {
mCommandListAdapter.mSelected = n;
//update();
}
void update() {
mListViewOfCommands.smoothScrollToPosition(getSelectionPos());
mCommandListAdapter.notifyDataSetChanged();
}
and the list item XML (res/layout/mylistitem.xml) is:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/mytext"
/>
</LinearLayout>
<View
android:id="#+id/selectionSign"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#10ff5000"
android:visibility="invisible"
/>
</FrameLayout>
The downside is that after changing the selection I have to update(): there is no easy way to unhighlight the previously highlighted view.
Set item state in selector like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true"
android:color="#color/XXX/>
</selector>
I don't know why state_checked not working. At first I thought it must be a Checkableview then I tried CheckedTextView. Still not working.
Anyway, state_activated will solve the problem.
I think you cann't use mListView inside anonymous class. You must use AdapterView arg0
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view,
int position, long id) {
AnyObject obj=(AnyObject)arg0.getItemAtPosition(position);
........
.........
.........
}
});
This post has worked out for me: http://www.mynewsfeed.x10.mx/articles/index.php?id=14
The ListView (inside layout.xml):
<ListView
android:id="#+id/list"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="#drawable/list_custom"
android:choiceMode="singleChoice"/>
Selector drawable(list_custom.xml):
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#color/orange"/>
<item android:state_activated="true" android:drawable="#color/blue" />
<item android:drawable="#color/green" />
</selector>
Color.xml in values folder:
<resources>
<item name="blue" type="color">#FF33B5E5</item>
<item name="green" type="color">#FF99CC00</item>
<item name="orange" type="color">#FFFFBB33</item>
</resources>
So I have a ListView and I want to change the color of each items background and text. This ListView is inside a ListFragment. My code inflates the layout in the onCreateView and inflates the layout of each item in the newView.
The android:state_pressed="true" is working fine, whenever I press in one item the background changes to that color. But when selecting an item neither the bg color or text color changes, even though I've defined an item with android:state_selected="true" in the selector.
Edit: I'm using SDK level 11 (Android 3.0) and a Motorola Xoom.
The list fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The list item layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="25dp"
android:background="#drawable/list_item_bg_selector">
<TextView android:id="#+id/form_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/text_size_xlarge"
android:textStyle="bold"
android:textColor="#drawable/list_item_text_selector" />
<TextView android:id="#+id/form_subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/text_size_medium"
android:textStyle="normal"
android:layout_marginTop="5dp"
android:textColor="#drawable/list_item_text_selector" />
</LinearLayout>
The background selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#color/white" />
<item
android:state_selected="true"
android:drawable="#drawable/list_item_bg_selected" />
<item
android:drawable="#color/list_bg" />
</selector>
The text selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="#color/white" />
<item
android:drawable="#color/list_text_blue" />
</selector>
The answer is to use the android:state_activated="true" state, instead of the "selected" state. More on this here: ListFragment Item Selected Background
The best solution with support of all API levels is to implement Checkable feature for list item View which means that the top view of your list item layout has to implement Checkable interface (in my case it was TextView, but the same can be applied on ViewGroup classes like LinearLayout). When you click on a list item, the ListView call setChecked method and there we change the state of View to use android:state_checked="true" selector. Together with list view android:choiceMode="singleChoice" it will select only one item.
The trick is to override onCreateDrawableState method and set the checked state here for drawables. See example of SelectableTextView bellow. After the setChecked is called, the checked state is stored and called refreshDrawableState.
Example of SelectableTextView:
package com.example.widget.SelectableTextView;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.TextView;
public class SelectableTextView extends TextView implements Checkable {
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private boolean mChecked;
public SelectableTextView(Context context) {
super(context);
}
public SelectableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setSelected(!mChecked);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
Example of selectable_list_item.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<com.example.widget.SelectableTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/list_item_selector_foreground"
android:background="#drawable/list_item_selector_background"
tools:text="Item 1"/>
Example of list_item_selector_foreground.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- checked -->
<item android:color="#color/list_item_text_active" android:state_checked="true"/>
<item android:color="#color/list_item_text"/>
</selector>
Example of list_item_selector_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/list_item_background_selected" android:state_pressed="true"/>
<item android:drawable="#color/list_item_background_selected" android:state_focused="true"/>
<item android:drawable="#color/list_item_background_active" android:state_checked="true"/>
<item android:drawable="#color/list_item_background"/>
</selector>
Do not forget to set clickable="true" for the layout. This solved my problem.
List item layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/list_item_bg_selector"
android:clickable="true" >
<TextView
android:id="#+id/tvNewsPreviewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:ellipsize="end"
android:textSize="#dimen/news_preview_title_textsize"
android:textStyle="bold" />
</RelativeLayout>
Background selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="#color/black" />
<gradient android:startColor="#color/white" android:endColor="#color/white" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="#color/holo_gray_darker" />
<gradient android:startColor="#color/holo_gray_bright" android:endColor="#color/holo_gray_bright" />
</shape>
</item>
</selector>
#Andrew S: Along with using activated state in selector , activated state must be set to false for default case as shown in below selector code.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false"
android:state_activated="false"
android:color="#color/dark_text_blue"/>
<item android:state_pressed="true"
android:color="#color/red"/>
<item android:state_activated="true"
android:color="#color/red"/>
</selector>