I have an activity with a recyclerview and a button. In my recyclerview adapter in my onBindViewHolder, I set an onClickListener to the layout of the list item. When this is clicked, I want to pass the Contact (list item type) that was selected to my activity and then into a list so that it can be sent as a request to my server.
When the item is clicked, I want the colour to change to a highlighted blue colour, and for the button at the bottom of the screen to say "Continue - x contacts" (x = however many contacts are selected.) I had this working until I added the button to the activity.
The problem I'm having, is that the button isn't associating with the button ID, and it is crashing out after an item click because setText() cannot be called on a null object reference.
Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setText(java.lang.CharSequence)' on a null object reference
I don't understand why the button is null, unless it is the way that I'm calling my activity object from the adapter.
Appreciate any help or advice!
RecyclerViewAdapter: (I have the other methods and ViewHolder class in the adapter and they are working fine. This is the only method that is effecting the result afaik)
private CreateGroupAccountStage2 createGroupAccountStage2 = new CreateGroupAccountStage2();
public List<Contacts> contactsList;
public List<Contacts> selectedContacts = new ArrayList<>();
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, final int position) {
final Contacts contacts = contactsList.get(position);
viewHolder.contactName.setText(contacts.getContactName());
viewHolder.contactItem.setOnClickListener(new View.OnClickListener() { //Set on click listener to item layout
#Override
public void onClick(View view) {
if (!contacts.getIsPressedValue()) {
contacts.setPressedTrue(); // Method that changes boolean value stored in the Contact object
viewHolder.contactItem.setBackgroundResource(R.color.createGroupAccountContactItemPressed);
selectedContacts.add(contacts);
} else {
viewHolder.contactItem.setBackgroundResource(R.color.whiteText);
contacts.setPressedFalse(); // Method that changes boolean value stored in the Contact object
selectedContacts.remove(contacts);
}
createGroupAccountStage2.updateSelectedContacts(contactsList);
}
});
}
Activity:
Button stage2Continue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_group_stage2);
stage2Continue = (Button) findViewById(R.id.createGroupAccountStage2ContinueBTN);
setUpActionBar();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ArrayList<Contacts> fullContactsList = new ArrayList<>();
fullContactsList.add(new Contacts(R.drawable.human_photo, "Human One", "humanone#gmail.com"));
fullContactsList.add(new Contacts(R.drawable.human_photo, "Human Two", "humantwo#gmail.com"));
setUpContactsRecyclerView(fullContactsList); //Sets up recycler view (works fine)
}
public void updateSelectedContacts(List<Contacts> contacts) {
int listSize = contacts.size();
updateContinueButton(listSize);
}
public void updateContinueButton(int selectedContactsListSize) {
String listSize = Integer.toString(selectedContactsListSize);
String buttonText = "Continue - " + listSize + " contacts";
if(selectedContactsListSize > 0) {
stage2Continue.setText(buttonText);
} else {
stage2Continue.setVisibility(View.INVISIBLE);
}
}
Layout XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="grouppay.dylankilbride.com.activities.CreateGroupAccountStage2">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ToolbarTheme">
<android.support.v7.widget.Toolbar
android:id="#+id/createAccountStage2Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/whiteText"
app:popupTheme="#style/ToolbarTheme" />
</android.support.design.widget.AppBarLayout>
<Button
android:id="#+id/createGroupAccountStage2ContinueBTN"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_margin="40dp"
android:layout_alignParentBottom="true"
android:text="#string/generic_continue"
android:textSize="15sp"
android:visibility="invisible"
android:textColor="#color/whiteText"
android:background="#drawable/generic_rounded_continue_button_bg"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/createGroupAccountStage2ContactsRV"
android:layout_width="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
Contact List Item Layout XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="70dp"
android:gravity="center_vertical"
android:padding="10dp"
android:id="#+id/createGroupAccountStage2ContactLL">
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/createGroupAccountStage2ContactImgTV"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/human_photo"
app:civ_border_width="1dp"
app:civ_border_color="#color/profileImageBorder"/>
<TextView
android:id="#+id/createGroupAccountStage2ContactNameTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textSize="20sp"
android:fontFamily="#font/cairo"
tools:text="Human Example"
/>
</LinearLayout>
It seems you need to implement onCreateViewHolder() in your RecyclerViewAdapter so that the ViewHolder is inflated with the layout including your createGroupAccountStage2ContinueBTN button
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = mInflater.inflate(R.layout.your_item_layout, parent, false);
return new ViewHolder(itemView);
}
Check your import, if correct contactName view is imported in your code, there is a chance that you are using same id for another view in any other xml file and because of that wrong import you are getting null for that view as that view is not inflated.
Related
I have a page in which I'm taking the START TIME and END TIME from DATABASE.
Let's say the START TIME is 7:00 and END TIME is 22:00
I want to use this START TIME and END TIME to show in my page as textview like 7:00 8:00 9:00 and sooo on till 22:00 as textview
Also I have an imageview that will also increase when the text increases.
How can I achieve this?
Also I want the result text in Horizontal Scroll View with Imageview at top and text view as bottom of each imageview
char first = StartTime.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
int l;
for( l = StartTimeint; l<=22; l++){
Log.d("SeatsPage", "Time is "+l);
}
timeofseats.setText(Integer.toString(l));
This is I have done so far but I'm getting 23 as a result, the textview is not increasing
This is my XML File
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/llMain"
android:layout_height="match_parent"
tools:context=".SeatsPagewithDB.SeatsPage">
<ImageView
android:id="#+id/imageView11"
android:layout_width="150px"
android:layout_height="150px"
android:layout_marginStart="28dp"
android:layout_marginEnd="326dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/seat" />
<TextView
android:id="#+id/timeofseats"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="334dp"
android:background="#FF0000"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="7:00"
android:textColor="#fff"
android:textSize="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView11" />
</android.support.constraint.ConstraintLayout>
This is the result I am getting as layout
This what I want programmatically
The XML code that you write in your layout.xml file to create the UI is for static UI only. What you are asking is to create views dynamically during runtime. Although you can definitely create views using java code on a click of a button or something. But it is better to code less for the UI whenever possible and keep it separated from the program code. Instead use the tools given to us by the framework we are using.
In Android those tools include stuff like ListView, GridView and the newer and better RecyclerView. These views help you add other views dynamically to your UI in runtime. You define one of them or more (depending on your UI needs) once in your layout.xml and configure them using java code like any other view.
This is how you can use RecyclerView to achieve your goal. I can't explain everything how RecyclerView works and what each line of code does as it will make a very long post but I have tried to highlight main things briefly.
1. Add RecyclerView in your layout file.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. Create another layout file and define the template UI of the item that the RecyclerView is going to display. RecyclerView will populate each item that it holds with this layout.
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="#+id/imageView_alarm"
android:layout_width="90dp"
android:layout_height="90dp"
android:src="#drawable/alarm" />
<TextView
android:id="#+id/textView_Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="#FF0000"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:text="Time"
android:textColor="#android:color/background_light"
android:textSize="24sp" />
</LinearLayout>
3. Create a ViewHolder class that extends from RecyclerView.ViewHolder. View holder is a RecyclerView related concept. In short it works as a wrapper around the view of a single item and aids in binding new data to the view of the item. Create a bind() function inside view holder to make your life easier.
EDIT: I have updated the class by implementing the View.OnClickListener interface, modified the constructor to pass in the context from onCreateViewHolder() and adding a setItemPosition() just for the sake to pass the item position number from onBindViewHolder() all over to here so we can use this position number in our onClick() method of the interface
MyViewHolder.java [UPDATED]
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textView;
private int itemPosition;
private Context mContext;
public MyViewHolder(#NonNull View itemView, Context context) {
super(itemView);
itemView.setOnClickListener(this);
mContext = context;
textView = itemView.findViewById(R.id.textView_Time);
}
void bind(String timeText)
{
textView.setText(timeText);
}
void setItemPosition(int position)
{
itemPosition = position;
}
#Override
public void onClick(View v) {
Toast.makeText(mContext, "You clicked item number: " + itemPosition , Toast.LENGTH_SHORT).show();
}
}
4. Create an Adapter class that extends from RecyclerView.Adapter. Adapter works as a bridge between the UI data and RecyclerView itself. An Adapter tells the RecyclerView what layout file to inflate and how many to inflate. RecyclerView job is to deal with how to inflate it on the UI.
EDIT : Just changed myViewHolder in onCreateViewHolder() to match the modified constructor of MyViewHolder. Added the call to setItemPosition() in the onBindViewHolder().
MyAdapter.java [UPDATED]
public class MyAdapter extends RecyclerView.Adapter {
List<String> timeIntervalList = new ArrayList<>();
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view , parent.getContext());
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
MyViewHolder viewHolder = (MyViewHolder) holder;
viewHolder.setItemPosition(position);
viewHolder.bind(timeIntervalList.get(position));
}
#Override
public int getItemCount() {
return timeIntervalList.size();
}
public void addItem (String timeText)
{
timeIntervalList.add(timeText);
notifyItemInserted(getItemCount());
}
}
In this adapter you will see two functions. OnCreateViewHolder() inflates the view using the template layout file for a single item and OnBindViewHolder() binds new data to the default values of the of the view just created. The data used for binding is stored in a list inside this Adapter called the timeIntervalList. This list will hold your time interval strings so they can be updated on the view.
5. Finally, use this RecyclerView where you want to use it. Like in your MainActivity.java. RecyclerView needs to be told in what fashion to display the items (e.g list , grid etc ) using a LayoutManager. LinearLayoutManager will display items either vertically or horizontally. You can see I am using your logic to increment time from string and adding new views to RecyclerView using the addItem() function of the MyAdapter class.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView myRecyclerView;
private MyAdapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myRecyclerView = findViewById(R.id.recyclerView);
myAdapter = new MyAdapter();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this , LinearLayoutManager.HORIZONTAL, false);
myRecyclerView.setLayoutManager(linearLayoutManager);
myRecyclerView.setAdapter(myAdapter);
// This is how you will populate the recycler view
String START_TIME = "7:00";
String END_TIME = "22:00";
char first = START_TIME.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
int l;
for( l = StartTimeint; l<=22; l++){
// This is where new item are added to recyclerView.
myAdapter.addItem(l + ":00");
}
}
}
This is the final result.
Change your activity layout XML code as follows,
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/llMain"
android:layout_height="match_parent"
tools:context=".SeatsPagewithDB.SeatsPage">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
...>
<LinearLayout
android:id="#+id/container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</HorizontalScrollView>
</android.support.constraint.ConstraintLayout>
Move the textview and imageview to another XML file let's call it item_view.xml (you can name it whatever you wish). we are doing so because the root view of this file will be reused.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView11"
android:layout_width="150px"
android:layout_height="150px"
app:srcCompat="#drawable/seat"/>
<TextView
android:id="#+id/timeofseats"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FF0000"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="7:00"
android:textColor="#fff"
android:textSize="20dp"/>
</LinearLayout>
Now make following changes in your Java file
LinearLayout container = findViewById(R.id.container); // or rootView.findViewById() for custom View and Fragment
char first = StartTime.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
for(int l = StartTimeint; l<=22; l++){
Log.d("SeatsPage", "Time is "+l);
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.item_view, null);
TextView timeofseats = view.findViewById(R.id.timeofseats);
timeofseats.setText(Integer.toString(l));
container.addView(view);
}
I've investigated several SO answers on this question (here, here, and here) and none of the proposed solutions have worked. My problem is that my RecyclerView list items aren't being displayed. I've set breakpoints in MessengerRecyclerAdapter, onCreateViewHolder, onBindViewHolder, and getItemCount and only the first one is ever called. While in a breakpoint I've entered the expression evaluator and executed
MessengerRecyclerAdapter.getItemCount();
And received the expected answer of 20. The RecyclerView itself takes up the intended content area as demonstrated by the screenshot below (I turned the RecyclerView magenta to highlight the space it occupies).
My RecyclerView XML code is below:
<?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.support.v7.widget.RecyclerView
android:id="#+id/thread_list"
android:background="#color/colorAccent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:name="com.jypsee.jypseeconnect.orgPicker.MessengerListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"
tools:context="com.jypsee.jypseeconnect.orgPicker.MessengerListFragment"
tools:listitem="#layout/fragment_messenger_cell"/>
</LinearLayout>
My RecyclerView Cell XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="#color/blueText"/>
<TextView
android:id="#+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="#color/darkText"/>
</LinearLayout>
My ListFragment class:
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
List<DummyContent.DummyItem> items = new ArrayList<>();
for (Integer i = 0; i<20; i++){
DummyContent.DummyItem item = new DummyContent.DummyItem(i.toString(),"Content","Details");
items.add(item);
}
View view = inflater.inflate(R.layout.fragment_messenger_list, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.thread_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerAdapter = new MessengerThreadRecyclerAdapter(items, mListener);
mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerAdapter.notifyDataSetChanged();
return view;
}
My Adapter class:
public class MessengerRecyclerAdapter
extends RecyclerView.Adapter<MessengerRecyclerAdapter.MessageThreadHolder>{
private final List<DummyItem> mValues;
private final RecyclerViewClickListener mListener;
public MessengerRecyclerAdapter(List<DummyItem> items, RecyclerViewClickListener listener) {
mValues = items;
mListener = listener;
}
#Override
public MessageThreadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_messenger_cell, parent, false);
return new MessageThreadHolder(view);
}
#Override
public void onBindViewHolder(final MessageThreadHolder holder, final int position) {
holder.mItem = mValues.get(position);
holder.mIdView.setText(mValues.get(position).id);
holder.mContentView.setText(mValues.get(position).content);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mListener != null) {
mListener.recyclerViewListClicked(v, position);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
public class MessageThreadHolder extends RecyclerView.ViewHolder {
public final View mView;
public final TextView mIdView;
public final TextView mContentView;
public DummyItem mItem;
public MessageThreadHolder(View view) {
super(view);
mView = view;
mIdView = (TextView) view.findViewById(R.id.id);
mContentView = (TextView) view.findViewById(R.id.content);
}
}
}
As you can see I've set the linearLayout orientation to vertical and set the layout manager, which were the 2 most common solutions. I'm really at a loss as to what to try next, so any help is appreciated.
As I said in previous Answer edit. The issues was in your xml. The main reason it was not showing was because, You were trying to add the fragment using include tag instead of fragment tag thus respective fragment class was never getting called on the fragment layout being added to your activity.
Below is the code you needed to add the Fragment correctly.
<fragment
android:id="#+id/message_thread"
android:name="com.jypsee.jypseeconnect.orgPicker.MessengerThreadListFragment"
layout="#layout/fragment_messengerthread_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/fragment_messengerthread_list" />
And your fragment layout should be like this
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
tools:context=".orgPicker.MessengerThreadListFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/thread_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Here is the screenshot of working
In my case , I was missing calling notifyDataSetChanged() after setting list in adapter.
I am taking data from my database using and want to set that as my list item. Upon clicking and holding an item, I want a dialog to open prompting the user to select one of the given options. My code is as follows:
My MainActivity.java class
public class MainActivity extends AppCompatActivity {
ListView todoList;
DbHelper dbHelper;
ArrayAdapter<String> myAdapter;
Button rename, delete;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper=new DbHelper(this);
todoList=(ListView)findViewById(R.id.to_do);
loadTaskList();
todoList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int i, long l) {
Dialog dialog=new Dialog(MainActivity.this);
dialog.setContentView(R.layout.item_dialog);
dialog.setTitle("Select");
rename = dialog.findViewById(R.id.rename_task);
delete = dialog.findViewById(R.id.delete_task);
dialog.show();
return false;
}
});
}
private void loadTaskList() {
ArrayList<String> taskList=dbHelper.getTask();
if(myAdapter==null){
myAdapter=new ArrayAdapter<String>(this,R.layout.todo_item,R.id.task_title,taskList);
todoList.setAdapter(myAdapter);
}else {
myAdapter.clear();
myAdapter.addAll(taskList);
myAdapter.notifyDataSetChanged();
}
}
My activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context="com.example.mylist.MainActivity">
<ListView
android:id="#+id/to_do"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"/>
</LinearLayout>
My Layout for the list item:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
<TextView
android:id="#+id/task_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Task"
android:textSize="20sp"
android:layout_centerVertical="true"
android:padding="5dp"/>
<ImageButton
android:id="#+id/task_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/ic_delete_black_24dp" />
<ImageButton
android:id="#+id/task_done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_done_black_24dp"
android:layout_toLeftOf="#id/task_delete"/>
</RelativeLayout>
Try like this;
todoList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Dialog dialog=new Dialog(MainActivity.this);
dialog.setContentView(R.layout.item_dialog);
dialog.setTitle("Select");
rename = dialog.findViewById(R.id.rename_task);
delete = dialog.findViewById(R.id.delete_task);
dialog.show();
}
});
To detect that an item is clicked, you custom adapter must know which one so you should add this in your adapter class.
public ItemClicked getItem(int position){
return items.get(position);
}
I think returning true from your OnItemLongClick() method rather than false should work.
You are saying that you are trying to do onItemClickListener.
But you have written onItemLongClickListner.
I have a main activity (ActivityMain.java) that I would like to use to navigate between four fragments. In one of these fragments, I'm attempting to place a CardView in conjunction with a RecyclerView to create a vertical list of cards. However, so far I've been unable to get any of the cards to display when I run the app. The CardView will display perfectly in Android Studio's design preview, but when an actual device/emulator is used it disappears.
I've tried to manually set the visibility of the CardView through Java, but it continued to stay invisible. I believe that the fragment's layout could be covering the CardView's layout, but I'm still very new to Android development so I'm not completely sure what the problem could be.
Below I've pasted the classes and XML files that are likely to be associated with my problem.
AdapterMainFeed.java
public class AdapterMainFeed extends RecyclerView.Adapter<AdapterMainFeed.ViewHolderMainFeed> {
private ArrayList<Article> listArticlesMain = new ArrayList<>();
private LayoutInflater inflater;
public AdapterMainFeed(Context context) {
inflater = LayoutInflater.from(context);
}
public void setArticlesMain(ArrayList<Article> listArticlesMain) {
this.listArticlesMain = listArticlesMain;
notifyDataSetChanged();
}
#Override
public ViewHolderMainFeed onCreateViewHolder(ViewGroup container, int i) {
View layout = inflater.inflate(R.layout.view_main_feed, container, false);
Article article1 = new Article("asdf", "ghj", "klm", new Date(0));
Article article2 = new Article("sdfg", "hjk", "lmn", new Date(0));
Article article3 = new Article("dfgh", "jkl", "mno", new Date(0));
listArticlesMain.add(article1);
listArticlesMain.add(article2);
listArticlesMain.add(article3);
return new ViewHolderMainFeed(layout);
}
#Override
public void onBindViewHolder(ViewHolderMainFeed viewHolderMainFeed, int i) {
Article currentArticle = listArticlesMain.get(i);
viewHolderMainFeed.articleTitle.setText(currentArticle.getTitle());
viewHolderMainFeed.articleAuthor.setText(currentArticle.getAuthor());
viewHolderMainFeed.articleWebsite.setText(currentArticle.getWebsite());
DateFormat formatter = DateFormat.getDateTimeInstance();
final String timePosted = formatter.format(currentArticle.getTimePosted());
viewHolderMainFeed.articleTime.setText(timePosted);
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public int getItemCount() {
return listArticlesMain.size();
}
static class ViewHolderMainFeed extends RecyclerView.ViewHolder {
TextView articleTitle;
TextView articleAuthor;
TextView articleWebsite;
TextView articleTime;
public ViewHolderMainFeed(View itemView) {
super(itemView);
articleTitle = (TextView) itemView.findViewById(R.id.mainArticleTitle);
articleAuthor = (TextView) itemView.findViewById(R.id.mainArticleAuthor);
articleWebsite = (TextView) itemView.findViewById(R.id.mainArticleWebsite);
articleTime = (TextView) itemView.findViewById(R.id.mainArticleTime);
}
}
}
view_main_feed.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
tools:context="com.convergeapp.converge.ActivityMain">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
card_view:cardCornerRadius="7dp"
card_view:cardBackgroundColor="#color/colorPurpleSeance"
android:id="#+id/mainArticleCard"
android:layout_width="match_parent"
android:layout_height="300dp"
android:padding="8dp"
android:clickable="true">
<RelativeLayout
android:id="#+id/mainArticleLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="#+id/mainArticleTitle"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
<TextView
android:id="#+id/mainArticleAuthor"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_below="#id/mainArticleTitle"/>
<TextView
android:id="#+id/mainArticleWebsite"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_below="#+id/mainArticleTime"/>
<TextView
android:id="#+id/mainArticleTime"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
After playing around with some changes, I finally discovered the solution. What I did was add the Article objects to the list in the main fragment's onCreateView method. Additionally, I instantiated CardView in the ViewHolder, though that didn't help me see the cards initially.
So, if you're ever in a similar predicament and can't figure out the solution, try to actually create the objects before setting them.
You need to call notifyDataSetChanged(); inside onCreateViewHolder after adding the new articles so that the itemcount is updated
Problem
I'm trying to create a ListView with selectable items. I want to be able to click on an item in the ListView and have the item change color in the list, and then go on and do something else with the data from the row.
I'm using a SimpleAdapter.
How do I make it so that when I tap on a row, it turns a different color, and then when I tap on a different row, the new row is selected and changed to a new color, and the old row changes back to normal?
Code
Here is my code so far. The DBTools class is has all of the data that I want to be displayed in my ListView organized and taken care of. The getAllReceivers() method returns an ArrayList of HashMap<String, String>s that have all of my data.
MainActivity.java:
public class MainActivity extends ListActivity {
DBTools dbTools = new DBTools(this);
ArrayList<HashMap<String, String>> receiverList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.activity_main);
receiverList = dbTools.getAllReceivers();
dbTools.close();
ListView listView = getListView();
if(receiverList.size() != 0) {
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] {"receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath});
setListAdapter(adapter);
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/black" >
<TextView
android:id="#+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="My List" />
</TableRow>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:id="#android:id/list" />
</TableLayout>
receiver_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tableRow" >
<TextView
android:id="#+id/receiverId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="#+id/receiverName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Robotronics" />
<TextView
android:id="#+id/fullPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123.45.678.910:8088/robtrox/find" />
</TableRow>
Solution
The solution to this problem is very simple. We need to add an OnItemClickListener to our ListView to listen for clicks and respond accordingly.
So, in the onCreate() method, once you've made sure that you set of data isn't empty, you're going to want to Override the onItemClick() method to listen for the click and change the color. You're also going to want to keep track of which item you selected for the later steps, so add public int selectionId = -1; at the top of your class. Furthermore, you'll need to let the ListAdapter know that you changed something by calling ((SimpleAdapter) getListAdapter()).notifyDataSetChanged().
if(receiverList.size() != 0) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
view.setBackgroundColor(Color.RED);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
selectionId = Integer.valueOf(receiverIdTextView.getText().toString());
((SimpleAdapter) getListAdapter()).notifyDataSetChanged();
}
});
SimpleAdapter adapter = getNewAdapter();
setListAdapter(adapter);
}
Great! Now we have a working system that will change the color of the row that you tap. But we're not done yet. We need to make sure that the previous selection changes back to the normal color.
For this, we are going to use override the SimpleAdapter's getView() method, which is called everytime the ListView goes to draw the items being displayed in it.
It only actually displays the items it needs to - the ones that you can see. It does not render the ones above or below your screen. So if you have 200 items in a ListView, only 5 or 6, depending on the size of your screen and the size of the items, are being rendered at a time.
To override the getView() method, go up to where you initialize the adapter and change the code to this:
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] { "receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath}) {
#Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
if(receiverIdTextView.getText().toString().equals(String.valueOf(selectionId))) {
view.setBackgroundColor(Color.RED);
} else {
view.setBackgroundColor(Color.WHITE);
}
return view;
}
};
Every time one of the rows is drawn, since the getView() will get called, the ListView will check if the current view has the id of row you selected. If it doesn't, it'll change the background color to white. If it does, it'll change the background color to red.
And voila! That's it! Now you are setting the background color to red when you click on an item in the ListView.
Final Code
MainActivity.java:
public class MainActivity extends ListActivity {
DBTools dbTools = new DBTools(this);
ArrayList<HashMap<String, String>> receiverList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.activity_main);
receiverList = dbTools.getAllReceivers();
dbTools.close();
ListView listView = getListView();
if(receiverList.size() != 0) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
view.setBackgroundColor(Color.RED);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
selectionId = Integer.valueOf(receiverIdTextView.getText().toString());
((SimpleAdapter) getListAdapter()).notifyDataSetChanged();
}
});
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] { "receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath}) {
#Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
if(receiverIdTextView.getText().toString().equals(String.valueOf(selectionId))) {
view.setBackgroundColor(Color.RED);
} else {
view.setBackgroundColor(Color.WHITE);
}
return view;
}
};
setListAdapter(adapter);
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/black" >
<TextView
android:id="#+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="My List" />
</TableRow>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:id="#android:id/list" />
</TableLayout>
receiver_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tableRow" >
<TextView
android:id="#+id/receiverId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="#+id/receiverName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Robotronics" />
<TextView
android:id="#+id/fullPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123.45.678.910:8088/robtrox/find" />
</TableRow>