I'm migrating from old ListView to RecyclerView in my project. I have decided to use Data Binding to bind list values, but I'm experiencing some problems to set custom text span for my text box.
Please look at this piece of code (this is how it's done with old ListBox adapater):
#Override
public View getView(int position, #Nullable View convertView, ViewGroup parent) {
...
Typeface tf = Typeface.createFromAsset(convertView.getContext().getAssets(), "fonts/fontawesome.ttf");
SpannableStringBuilder textCategory = new SpannableStringBuilder((item != null ? item.getCategoryIconName() : null) + " " + item.getCategoryName());
textCategory.setSpan (new CustomTypefaceSpan("", tf), 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
viewHolder.eventCategory.setText(textCategory);
...
}
Basically, my CustomTypeFaceSpan is used to set multiple fonts for a single TextView. How to implement the same with Data Binding? Is it possible to use BindingAdapter for that?
My try:
<variable name="categoryText" type="String"/>
<variable name="data" type="Event"/>
<TextView
android:id="#+id/event_category_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:paddingBottom="3dp"
android:paddingTop="3dp"
android:text="#{data.categoryIconName + data.categoryName}"
android:textAppearance="#style/TextAppearance.AppCompat.Body2"
android:textColor="#color/colorEventCategoryText"
app:customFunction="#{categoryText}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
Binding adapter:
#BindingAdapter({"customFunction"})
public static void setSpan(TextView textView, String categoryText)
{
Typeface tf = Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/fontawesome.ttf");
SpannableStringBuilder textCategory = new SpannableStringBuilder(categoryText);
textCategory.setSpan (new CustomTypefaceSpan("", tf), 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(textCategory);
}
Problem: categoryText is always null. Any ideas? TextView is bound properly, because without custom function it displays text as it should.
My list adapter:
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder>
{
public List<Event> events = null;
private final ListFragment.OnFragmentInteractionListener mListener;
public ListAdapter(List<Event> events, ListFragment.OnFragmentInteractionListener mListener)
{
this.events = events;
this.mListener = mListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
{
LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.cell, viewGroup, false);
return new ViewHolder(binding);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position)
{
final Event event = events.get(position);
viewHolder.bind(event);
viewHolder.binding.getRoot().setOnClickListener(v ->
{
if (null != mListener)
{
mListener.onFragmentInteraction(event);
}
});
}
#Override
public int getItemCount()
{
return events.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder
{
private final ViewDataBinding binding;
public ViewHolder(ViewDataBinding binding)
{
super(binding.getRoot());
this.binding = binding;
}
public void bind(Object obj)
{
binding.setVariable(BR.data,obj);
binding.executePendingBindings();
}
}
}
Use custom attribute like:
In xml:
<data>
<variable
name="name"
type="String"></variable>
</data>
........
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
app:customFunction="#{name}"
/>
In Activity Class
.............
ActivityMainBinding binding = DataBindingUtil........
binding.setName("Something");
...................
#BindingAdapter("customFunction")
public static void myCustomFunction(TextView textView, String name){
Log.d("MainActivity", "custom function called");
String nameCaps = name.toUpperCase();
textView.setText(nameCaps);
}
myCustomFunction(..) will be called when Textview is created from xml. You can use your custom function to set Span to your view.
Related
Im trying to create a bunch of rows in a Listview to show from an ArrayList of object class
my object class Locations has
private String name;
private int currentCapacity;
private int maxCapacity;
private int id;
public Locations(String name, int currentCapacity, int maxCapacity, int id){
this.currentCapacity = currentCapacity;
this.id = id;
this.maxCapacity = maxCapacity;
this.name = name;
}
Locations loc1 = new Locations("foodplace", 10, 100, 1);
Locations loc2 = new Locations("area", 15, 25, 2);
Locations loc3 = new Locations("otherplace", 25, 25, 3);
locationsArrayList.add(loc1);
locationsArrayList.add(loc2);
locationsArrayList.add(loc3);
ListView list = (ListView) findViewById(R.id.theList);
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, locationsArrayList);
list.setAdapter(adapter);
how do i input my ArrayList such that i can change the color of the row based on the currentCapacity and show the name on the block?
You have to use RecyclerView
//Declare some items in your ArrayList
Locations loc1 = new Locations("foodplace", 10, 100, 1);
Locations loc2 = new Locations("area", 15, 25, 2);
Locations loc3 = new Locations("otherplace", 25, 25, 3);
locationsArrayList.add(loc1);
locationsArrayList.add(loc2);
locationsArrayList.add(loc3);
//make your own adapter class which extend RecyclerView.Adapter
MyAdapter myAdapter = new MyAdapter(locationsArrayList,this);
//setup your RecylerView
recyclerView.setAdapter(itemsListAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
Now in your Adapter class you can change each item color or font... based on ArrayList attribute.
public class MyAdapter extends RecyclerView.Adapter{
private ArrayList<Location> locationsArrayList;
private Context context;
public MyAdapter(ArrayList<Location> locationsArrayList, Context context) {
this.locationsArrayList= locationsArrayList;
this.context = context;
}
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(context)
.inflate(R.layout.item_layout,parent,false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
//here you can set your own conditions based on your arraylist using position parameter
viewHolder.itemNameTextView.setText(locationsArrayList.get(position).getName());
}
#Override
public int getItemCount() {
return locationsArrayList.size();
}
public Context getContext() {
return context;
}
class ViewHolder extends RecyclerView.ViewHolder {
//setup item layout attributes
TextView itemNameTextView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemNameTextView = itemView.findViewById(R.id.itemNameTextView);
}
}
}
I know it's complicated but RecyclerView make your list more flexible.
Hie i would suggest you use RecyclerView to achieve your list view
if you are using gradle for your dependencies you need to implement it in your app level gradle file
In your activity.xml file you implement like this
<androidx.constraintlayout.widget.ConstraintLayout 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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/locations_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And then create a item file that will represent item in a list item
for example lets call it location_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
>
<TextView
android:id="#+id/location_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<TextView
android:id="#+id/max_capacity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<TextView
android:id="#+id/current_capacity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
Then after that you would need to implement an Adapter lets call it LocationAdapter.java
public class LocationAdapter extends RecyclerView.Adapter<LocationAdapter.ViewHolder> {
ArrayList<Location> locations;//list of locations
public LoacationAdapter(ArrayList<Location> locations) {
this.locations = locations //initialising list in constructor
}
#NonNull
#Override
public Locations.Adapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//here this method inflates the row view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.location_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull LocationAdapter.ViewHolder holder, int position) {
//this method sets values in the views
holder.locationName.settext(locations.get(position).getLocationName())
holder.maxCapacity.settext(locations.get(position).getMaxCapacity())
holder.currentCapacity.settext(locations.get(position).getCurrentCapacity())
}
#Override
public int getItemCount() {
return locatioins.size();// number of locations
}
//this class will repressent the list item
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// this are the views in the list item
TextView locationName, maxCapacity, currrentCapacity;
public ViewHolder(#NonNull View itemView) {
super(itemView);
locationName = itemView.findViewById(R.id.location_name);
maxCapacity= itemView.findViewById(R.id.max_capacity);
currentCapacity= itemView.findViewById(R.id.current_capacity);
itemView.setOnClickListener(this::onClick);
}
#Override
public void onClick(View v) {
//do what ever you want when an item is clicked
}
}
}
In in your activity file then
ArrayList<Location> locationsArraylist = new ArrayLists<> ();
//initializing recylcer view
RecyclerView locationList = findViewById(R.id.locations_list);
// setting layout manager
locationsList.setLayoutManager(new LinearLayoutManager(getContext, RecyclerView.Vertical, false));
//setting adapter adapter with the list of locations you want to display
locationList.setAdapter(new LocationsAdapter(locationsArraylist));
I want to create the following UI with a single list of dataset.
I tried using multiple view type but could not achieve my requirement. I also implemented this blog Android Horizontal and Vertical RecyclerView Example.
But this uses two recyclerviews and there are two sets of data (horizontal data and vertical data).
I also have tried this one. RecyclerView with multiple views using custom adapter in Android
But this is using static card views in XML and loading them in adapter.
I'm beginner in Android development. Please help!
Thank you in advance.
Your desired layout can be achieved by using a GridLayoutManager along with two "item view types" inside your RecyclerView.Adapter.
Here are my layout XML files:
activity_main.xml:
-------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
horizontal.xml:
-------------------------
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/image"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ccc"/>
<TextView
android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:background="#fff"
android:textColor="#000"
android:textSize="18sp"
android:text="TEXT"/>
</LinearLayout>
</android.support.v7.widget.CardView>
vertical.xml:
-------------------------
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ccc"/>
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:background="#fff"
android:textColor="#000"
android:textSize="18sp"
android:text="TEXT"/>
</LinearLayout>
</android.support.v7.widget.CardView>
And here is my Java file:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridLayoutManager manager = new GridLayoutManager(this, 2);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return (position % 4) < 2 ? 2 : 1;
}
});
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setLayoutManager(manager);
recycler.setAdapter(new MyAdapter());
}
private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
#Override
public int getItemViewType(int position) {
return (position % 4) < 2
? R.layout.horizontal
: R.layout.vertical;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(viewType, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.image.setImageResource(R.drawable.mouse);
holder.text.setText("" + position);
}
#Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
}
private static class MyViewHolder extends RecyclerView.ViewHolder {
private final ImageView image;
private final TextView text;
public MyViewHolder(View itemView) {
super(itemView);
this.image = (ImageView) itemView.findViewById(R.id.image);
this.text = (TextView) itemView.findViewById(R.id.text);
}
}
}
Let's go over the important parts. First up is the combination of GridLayoutManager and SpanSizeLookup. We're creating the layout manager with this line:
GridLayoutManager manager = new GridLayoutManager(this, 2);
Which means that, by default, there will be two cards in each row of our grid. But then we apply the SpanSizeLookup, which says that half of our rows (found by the statement position % 4 < 2) should actually take up two columns. So we'll have one card, one card, two cards repeating in our "grid".
Then, in the RecyclerView.Adapter class, we override the getItemViewType() method. Here we again use the position % 4 < 2 statement to say that half of our views should be horizontal, and half should be vertical.
getItemViewType() just needs to return any unique int for each view type, so we use a nice trick of returning R.layout constants from this method. Since the view type will then be passed into onCreateViewHolder(), we can use the viewType argument to inflate the correct layout.
And that's it! Not too bad after all. Here's a screenshot of my code in action:
I had this issue not too long ago, though I may not be an expert I think my answer can help you as well. You can do this by creating two different types of layouts, similar to the article. You need to create an abstract class that both of the layout types can extend. Then, in your adapter, check what type of object should be shown.
Abstract class:
public abstract class ListItem {
public static final int TYPE_HORIZONTAL = 0;
public static final int TYPE_VERTICAL = 1;
abstract public int getType();
}
Horizontal Item:
public class HorizontalItem extends ListItem {
private String text;
private Bitmap image;
public HorizontalItem(String text, Bitmap image) {
this.text = text;
this.image = image;
}
/*
* Getter and setter methods here
*/
#Override
public int getType() {
return ListItem.TYPE_HORIZONTAL;
}
}
Vertical Item:
public class VerticalItem extends ListItem {
private String text1;
private Bitmap image1;
private String text2;
private Bitmap image2;
public VerticalItem(String text1, String text2, Bitmap image1, Bitmap image2) {
this.text1 = text1;
this.image1 = image1;
this.text2 = text2;
this.image2 = image2;
}
/*
* Getter and setter methods here
*/
#Override
public int getType() {
return ListItem.TYPE_HORIZONTAL;
}
}
Adapter:
public class ListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<ListItem> listItems;
public static class HorizontalViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public ImageView imageView;
public HorizontalHolder(View v) {
super(v);
text = (TextView) v.findViewById(R.id.text);
imageView = (ImageView) v.findViewById(R.id.imageView);
}
}
public static class VerticalViewHolder extends RecyclerView.ViewHolder {
public TextView text1;
public ImageView imageView1;
public TextView text2;
public ImageView imageView2;
public HorizontalHolder(View v) {
super(v);
text1 = (TextView) v.findViewById(R.id.text1);
imageView1 = (ImageView) v.findViewById(R.id.imageView1);
text2 = (TextView) v.findViewById(R.id.text2);
imageView2 = (ImageView) v.findViewById(R.id.imageView2);
}
}
// constructor
public ListAdapter(ArrayList<ListItem> listItems) {
this.listItems = listItems;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
RecyclerView.ViewHolder holder = null;
switch (viewType) {
case ListItem.TYPE_HORIZONTAL:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_horizontal, parent, false);
holder = new HorizontalViewHolder(v);
break;
case ListItem.TYPE_VERTICAL:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_vertical, parent, false);
holder = new VerticalViewHolder(v);
break;
}
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case ListItem.TYPE_HORIZONTAL:
((HorizontalViewHolder) holder).text.setText(listItems.get(position).getText());
((HorizontalViewHolder) holder).imageView.setImageBitmap(listItems.get(position).getBitmap());
// the getText() and getBitmap() methods come from the getters of the HorizontalItems and VerticalItems that are stored in the ArrayList, listItems
break;
case ListItem.TYPE_DECK:
// Identical to above
break;
}
}
#Override
public int getItemCount() {
return listItems.size();
}
// This is extremely important, it is what lets the adapter know what type each listItem element is
#Override
public int getItemViewType(int position) {
return listItems.get(position).getType();
}
}
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
// Must be of abstract type ListItem
ArrayList<ListItem> items = new ArrayList<>();
// populate your ArrayList
items.add(new HorizontalItem("text", bitmap));
items.add(new VerticalItem("text1", "text2", bitmap1, bitmap2));
// ... and so on
ListAdapter adapter = new ListAdapter(items)
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setAdapter(adapter);
recycler.setLayoutManager(new LinearLayoutManager(this));
// ... the rest of your code below
}
In your layout for the vertical list item, I would just create one file with two halves to it. You can use a LinearLayout to easily divide the subsections into perfect halves.
I'm aware of a few ways to populate an Android ListView object with a title and a icon but I feel stuck when trying to simplify and improve my code.
This is the scenario:
1 - Creating two arrays
2 - Creating a RowItem class
public class IconRow {
private String title;
private int icon;
public IconRow(String title, int icon) {
this.title = title;
this.icon = icon;
}
public String getTitle() {
return title;
}
public int getIcon() {
return icon;
}
}
3 - Creating a ListAdapter
public class ListAdapter extends BaseAdapter {
Context context;
List<IconRow> rowItem;
String description;
long option;
public ListAdapter(Context context, List<IconRow> rowItem, String description, long option)
{
this.context = context;
this.rowItem = rowItem;
this.description = description;
this.option = option;
}
#Override
public int getCount() {
return rowItem.size();
}
#Override
public Object getItem(int position) {
return rowItem.get(position);
}
#Override
public long getItemId(int position) {
return rowItem.indexOf(getItem(position));
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.lv_arrow, null);
ImageView imgIcon = (ImageView) convertView.findViewById(R.id.imageView1);
TextView txtTitle = (TextView) convertView.findViewById(R.id.textView1);
IconRow row_pos = rowItem.get(position);
// setting the image resource and title
imgIcon.setImageResource(row_pos.getIcon());
txtTitle.setText(row_pos.getTitle());
}
return convertView;
}
4 - Creating a Row Layout
<?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="horizontal"
android:background="#android:color/white" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:layout_marginRight="10dp"
android:fontFamily="sans-serif-light"
android:text="TextView"
android:textColor="#android:color/black"
android:textSize="24dp" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="right|center"
android:layout_marginLeft="20dp"
android:scaleType="fitEnd"
android:src="#drawable/ic_go"
android:layout_marginRight="0dp" />
</LinearLayout>
5 - Creating the List inside Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_parameters_settings, container, false);
lv_settings_1 = (ListView) rootView.findViewById(R.id.lv_settings_1);
menutitles = getResources().getStringArray(R.array.array_lv_settings_1);
menuIcons = getResources().obtainTypedArray(R.array.arrow_icons);
menu_iconRow = new ArrayList<>();
for (int i = 0; i < menutitles.length; i++) {
IconRow items = new IconRow(menutitles[i], menuIcons.getResourceId(
i, -1));
menu_iconRow.add(items);
}
adapter_settings_1 = new ListAdapter(getActivity(), menu_iconRow, "No Description", 0);
lv_settings_1.setAdapter(adapter_settings_1);
return rootView;
}
Am I doing it the wrong way? Is there anyway I can simplify this code?
I usually use your way for manage ListView. Probably you know this but give a look at this page http://developer.android.com/training/improving-layouts/smooth-scrolling.html
Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.
static class ViewHolder {
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}
I'm learning Android SDK and I need some advices.
I have custom ListView with BaseAdapter and I want to implement some new feature - Favorite Button.
What I want to do is, when I press the Favorite Button, ListItem goes to the beginning of the list, Favorite image change and all that stuff will be saved in the SharedPrefs.
Someone tell me what I need to do, to make it works?
my existing code:
row.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="wrap_content"
android:id="#+id/layout_element_list"
>
<ImageView
android:id="#+id/icon"
android:layout_width="150dp"
android:padding="5dp"
android:layout_height="150dp"
android:layout_marginLeft="4px"
android:layout_marginRight="10px"
android:layout_marginTop="4px"
android:src="#drawable/radio" >
</ImageView>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/label"
android:paddingTop="20dp"
android:layout_gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:textAlignment="center"
android:text="RadioName"
android:textColor="#color/color1"
android:textSize="30dp" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<TextView
android:id="#+id/label2"
android:layout_gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_weight="1"
android:layout_height="fill_parent"
android:textAlignment="center"
android:text="Description.."
android:textColor="#color/color1"
android:textSize="15dp" />
<ImageView
android:id="#+id/favButton"
android:layout_weight="1"
android:layout_width="fill_parent"
android:padding="5dp"
android:layout_height="fill_parent"
android:layout_marginLeft="4px"
android:layout_marginRight="10px"
android:layout_marginTop="4px"
android:src="#drawable/fav_off" >
</ImageView>
</LinearLayout>
</LinearLayout>
</LinearLayout>
BaseAdapter class:
public class RadioAdapter extends BaseAdapter
{
ArrayList<RadioStation> myList = new ArrayList<RadioStation>();
LayoutInflater inflater;
Context context;
public RadioAdapter(Context context, ArrayList<RadioStation> myList) {
this.myList = myList;
this.context = context;
inflater = LayoutInflater.from(this.context);
}
#Override
public int getCount() {
return myList.size();
}
#Override
public RadioStation getItem(int position) {
return myList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyViewHolder mViewHolder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.activity_menu_row, null);
mViewHolder = new MyViewHolder();
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
mViewHolder.tvTitle = detail(convertView, R.id.label, myList.get(position).getTitle());
mViewHolder.tvDesc = detail(convertView, R.id.label2, myList.get(position).getDescription());
mViewHolder.ivIcon = detail(convertView, R.id.icon, myList.get(position).getImgResId());
return convertView;
}
private TextView detail(View v, int resId, String text) {
TextView tv = (TextView) v.findViewById(resId);
tv.setText(text);
return tv;
}
private ImageView detail(View v, int resId, int icon) {
ImageView iv = (ImageView) v.findViewById(resId);
iv.setImageResource(icon); //
return iv;
}
private class MyViewHolder {
TextView tvTitle, tvDesc;
ImageView ivIcon;
}
}
RadioStation class:
public class RadioStation
{
public String title;
public String description;
public int imgResId;
//getters and setters
public static Comparator<RadioStation> comparatorByRadioName = new Comparator<RadioStation>()
{
#Override
public int compare(RadioStation radioStation, RadioStation radioStation2)
{
String name1 = radioStation.getTitle().toLowerCase();
String name2 = radioStation2.getTitle().toLowerCase();
return name1.compareTo(name2);
}
};
}
ActivityListView:
public class ActivityMenuList extends Activity implements AdapterView.OnItemClickListener
{
private ListView lvDetail;
private Context context = ActivityMenuList.this;
private ArrayList <RadioStation> myList = new ArrayList <RadioStation>();
private String[] names = new String[] { "one", "two", "three" };
private String[] descriptions = new String[] { "notset", "notset", "notset"};
private int[] images = new int[] { R.drawable.one, R.drawable.two, R.drawable.three };
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getWindow().setBackgroundDrawableResource(R.drawable.bg1);
setContentView(R.layout.activity_menu_list);
lvDetail = (ListView) findViewById(R.id.list);
lvDetail.setOnItemClickListener(this);
getDataInList();
lvDetail.setAdapter(new RadioAdapter(context, myList));
}
private void getDataInList() {
for(int i=0;i<3;i++) {
RadioStation ld = new RadioStation();
ld.setTitle(names[i]);
ld.setDescription(descriptions[i]);
ld.setImgResId(images[i]);
myList.add(ld);
}
Collections.sort(myList, RadioStation.comparatorByRadioName);
}
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l)
{
String item = names[i];
Intent e = new Intent(ActivityMenuList.this, ActivityRadioStation.class);
Bundle data = new Bundle();
data.putString("radiostation",item);
e.putExtras(data);
startActivity(e);
}
}
That's a lot of changes you have to do. Let's start with the basic.
Add a boolean to your RadioStation for the favorite state.
public boolean isFavorite;
Next on your getView add the favorite button click listener(add its reference to the viewholder too, but let's keep it simple this time)
public class RadioAdapter extends BaseAdapter
{
ArrayList<RadioStation> myList = new ArrayList<RadioStation>();
LayoutInflater inflater;
Context context;
ListView mListview;
public RadioAdapter(Context context, ArrayList<RadioStation> myList, ListView list) {
this.myList = myList;
this.context = context;
mListView = list;
inflater = LayoutInflater.from(this.context);
}
#Override
public int getCount() {
return myList.size();
}
#Override
public RadioStation getItem(int position) {
return myList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
MyViewHolder mViewHolder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.activity_menu_row, null);
mViewHolder = new MyViewHolder();
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
mViewHolder.tvTitle = detail(convertView, R.id.label, myList.get(position).getTitle());
mViewHolder.tvDesc = detail(convertView, R.id.label2, myList.get(position).getDescription());
mViewHolder.ivIcon = detail(convertView, R.id.icon, myList.get(position).getImgResId());
convertView.findViewById(R.id.favButton).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
myList.get(position).isFavorite=! myList.get(position).isFavorite;
//reorder mlist
notifyDataSetChanged();
//mListView. smoothscroll here
}
});
((ImageView) convertView.findViewById(R.id.favButton)).setImageResource(myList.get(position).isFavorite?R.drawable.favoriteOn:R.drawable.favoriteOff);
return convertView;
}
private TextView detail(View v, int resId, String text) {
TextView tv = (TextView) v.findViewById(resId);
tv.setText(text);
return tv;
}
private ImageView detail(View v, int resId, int icon) {
ImageView iv = (ImageView) v.findViewById(resId);
iv.setImageResource(icon); //
return iv;
}
private class MyViewHolder {
TextView tvTitle, tvDesc;
ImageView ivIcon;
}
}
I left commented what you should do on the listener. You should be able to continue from here.
When you create your adapter pass the list as the last parameter on the constructor.
Edited: Removed interface. No need to use it here.
I'm trying to make 'dynamic' listview that contains 0-2 headers and 0-1 footer(based on the needed mode).
If I use the order
getListView().addFooterView(footerView, null, true);
getListView().setAdapter(myAdapter);
I receive an error:
java.lang.NullPointerException
at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:778)
at android.widget.ListView.addFooterView(ListView.java:364)
at com.my.fragments.QuestionListFragment.updateList(QuestionListFragment.java:63)
and if I switch the lines to
getListView().setAdapter(myAdapter);
getListView().addFooterView(footerView, null, true);
no errors, but footer view doesn't appear on mode change (it appears on the next mode change, so I assume the code is working).
footerView is not null at this moment in the runtime
I can't figure out what is the problem here, but I really need headers and footers to be part of the listview(I can make it with a ScrollView and separate frames, but that won't do)
Update: A thing to add: data which is shown in the list is being loaded asyncronously and is stored in the separate static ArrayList. I use setAdapter(null) on the loading start and setAdapter(myAdapter) on complete to modify the ListView. Maybe the problem is here?
Update2
view_question_list_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_marginTop="2dip" android:layout_marginBottom="2dip">
<TextView android:textAppearance="?android:attr/textAppearanceSmall" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:textStyle="bold" android:text="Load More Questions" android:gravity="center"
android:layout_gravity="center" android:lines="3" android:textColor="#android:color/white"></TextView>
</FrameLayout>
QuestionAdapter.java
public class QuestionAdapter extends BaseAdapter {
public class BrowseSettings {
public SortingMode sortingMode = SortingMode.SORT_BY_DATE;
public boolean isAnswered = true;
public int categoryFilter = -1;
public String textFilter = null;
}
private LayoutInflater inflater = null;
private BrowseSettings settings = new BrowseSettings();
private LoaderListener loaderListener = null;
public QuestionAdapter(Context context, LoaderListener listener) {
inflater = LayoutInflater.from(context);
loaderListener = listener;
}
private void loadData() {
QAData.questions.clear();
appendData();
}
public void appendData() {
new QuestionLoaderThread(settings.isAnswered, settings.sortingMode, settings.categoryFilter, settings.textFilter,
QAData.questions.getCount(), BaseRequest.DEFAULT_COUNT_VALUE, loaderListener).start();
}
public void applyCategoryFilter(int filter) {
settings.categoryFilter = filter;
loadData();
}
public void applyTextFilter(String filter) {
settings.textFilter = filter;
loadData();
}
#Override
public int getCount() {
return QAData.questions.getCount();
}
#Override
public Object getItem(int position) {
return QAData.questions.get(position);
}
#Override
public long getItemId(int position) {
return QAData.questions.get(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
QuestionViewHolder viewHolder = null;
if(convertView == null) {
convertView = inflater.inflate(R.layout.view_question, null);
viewHolder = new QuestionViewHolder();
viewHolder.content = (TextView) convertView.findViewById(R.id.QuestionContent);
viewHolder.likes = (TextView) convertView.findViewById(R.id.QuestionLikeCounter);
viewHolder.question = QAData.questions.get(position);
convertView.setTag(viewHolder);
} else {
viewHolder = (QuestionViewHolder) convertView.getTag();
}
viewHolder.content.setText(Html.fromHtml(viewHolder.question.getFormattedQuestion()));
viewHolder.likes.setText(String.valueOf(viewHolder.question.getLikesCount()));
return convertView;
}
private class QuestionViewHolder {
private TextView content;
private TextView likes;
private QAQuestion question;
}
}
Try using getListView().addFooterView(footerView); instead.