I use a RecyclerView in an Android app (API 23+) and update its adapter with around 380 items. Even though only 15 items are visible, the RecyclerView's adapter's onBindViewHolder() method gets called for all 380 items. Some threads describe that this happens when the RecyclerView is inside a ScrollView or NestedScrollView; however, that is not the case here. Another suggestion is to set the width of the recycler view to 0dp or to set its height to wrap-content. Neither tip works in this case.
Here is a graphic of the navigation graph:
Here is the code:
<?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="match_parent">
<!-- TABLE TITLE-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/mid_gray"
android:gravity="top"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<TextView
android:id="#+id/txt_table_title_0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="0.7"
android:text="#string/caja"
android:textStyle="bold" />
<TextView
android:id="#+id/txt_table_title_variedad"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/variedad"
android:textStyle="bold" />
<TextView
android:id="#+id/txt_table_title_date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/ingreso"
android:textStyle="bold" />
<TextView
android:id="#+id/txt_table_title_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".7"
android:text="#string/semanas"
android:textStyle="bold" />
<TextView
android:id="#+id/txt_table_title_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/cantidad"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".7" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Here is the adapter code
package com.plantecuador.poscosecha.adapter;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.plantecuador.poscosecha.R;
import com.plantecuador.poscosecha.db.entities.BoxInside;
import com.plantecuador.poscosecha.event.BoxInsideSelectedEvent;
import com.plantecuador.poscosecha.helper.DateFormatHelper;
import com.plantecuador.poscosecha.viewholder.ThrowOutViewHolder;
import org.greenrobot.eventbus.EventBus;
import org.threeten.bp.DateTimeUtils;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.ZoneId;
import org.threeten.bp.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import timber.log.Timber;
public class ThrowOutAdapter extends RecyclerView.Adapter<ThrowOutViewHolder> {
private ArrayList<BoxInside> insides = new ArrayList<>();
#NonNull
#Override
public ThrowOutViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_throw_out, parent, false);
return new ThrowOutViewHolder(view);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull ThrowOutViewHolder vh, int position) {
Timber.v("onBindViewHolder called in throwout and position %s", position);
if (position % 2 == 0) {
vh.rowLayout.setBackgroundColor(Color.WHITE);
} else {
vh.rowLayout.setBackgroundColor(Color.parseColor("#D8D8D8"));
}
BoxInside inside = insides.get(position);
vh.txtInsideNumber.setText(inside.getId());
Date date = (inside.getDateCFOrigPacked() == null) ? inside.getDatePacked() : inside.getDateCFOrigPacked();
String strDate = DateFormatHelper.formatToDateString("dd/MM/yy", date);
vh.txtDate.setText(strDate);
vh.txtQuantity.setText(inside.getQuantity() + "");
vh.txtVariety.setText(inside.getVariety() + "");
Instant instantStart = DateTimeUtils.toInstant(date);
LocalDateTime localStart = LocalDateTime.ofInstant(instantStart, ZoneId.systemDefault());
Instant instantEnd = DateTimeUtils.toInstant(new Date());
LocalDateTime localEnd = LocalDateTime.ofInstant(instantEnd, ZoneId.systemDefault());
long weeks = ChronoUnit.WEEKS.between(localStart, localEnd);
vh.txtAge.setText(weeks + "");
assert vh.btnVer != null;
vh.btnVer.setOnClickListener(view -> EventBus.getDefault().post(new BoxInsideSelectedEvent(inside)));
}
#Override
public int getItemCount() {
return insides.size();
}
public void update(List<BoxInside> insides) {
this.insides.clear();
this.insides.addAll(insides);
Timber.v("About to notify dataset changed");
notifyDataSetChanged();
}
}
The behavior is as follows: The adapter's update() method is called with a new list of BoxInside objects when there are updates. I'd like the RecyclerView to obviously clear, update it's dataset, and update the view but not worry about creating ViewHolders outside the visible area until the user scrolls.
Changing from a RelativeLayout to a FrameLayout fixes it. Here is the navigation hierarchy after the fix.
Related
I'm trying to add a Firebase Recyclerview in my Android App. When I add, all the data is getting fetched from Firestore normally, but when it comes to handle onClick event, it is not working at all.
Things I followed:
Added Interface with method.
Implemented interface in my TipsActivity.java
Here is the code:
TipsActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.Toast;
import android.util.Log;
import com.firebase.ui.firestore.paging.FirestorePagingOptions;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
public class TipsActivity extends AppCompatActivity implements FirestoreTipsAdapter.OnListItemClick {
FirestoreTipsAdapter firestoreTipsAdapter;
FirebaseFirestore firebaseFirestore;
RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tips);
firebaseFirestore = FirebaseFirestore.getInstance();
recyclerView = findViewById(R.id.list);
Query query = firebaseFirestore.collection("DailyTips").document("MyTips").collection("Tips");
PagedList.Config config = new PagedList.Config.Builder()
.setInitialLoadSizeHint(10)
.setPageSize(5)
.build();
FirestorePagingOptions<TipsModel> firestorePagingOptions = new FirestorePagingOptions.Builder<TipsModel>()
.setLifecycleOwner(this)
.setQuery(query,config,TipsModel.class)
.build();
firestoreTipsAdapter = new FirestoreTipsAdapter(firestorePagingOptions,this,this);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(firestoreTipsAdapter);
}
#Override
public void onItemClick() {
Toast.makeText(this, "Show up bruh!", Toast.LENGTH_SHORT).show();
Log.d("AT_LEAST","You should work");
}
}
And here goes my:
FirestoreTipsAdapter.java
package com.mycompany.company;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.firebase.ui.firestore.paging.FirestorePagingAdapter;
import com.firebase.ui.firestore.paging.FirestorePagingOptions;
public class FirestoreTipsAdapter extends FirestorePagingAdapter<TipsModel, FirestoreTipsAdapter.TipsViewHolder> {
private OnListItemClick onListItemClick;
Context context;
public FirestoreTipsAdapter(#NonNull FirestorePagingOptions<TipsModel> options,OnListItemClick onListItemClick,Context context) {
super(options);
this.onListItemClick = onListItemClick;
this.context = context;
}
#Override
protected void onBindViewHolder(#NonNull TipsViewHolder holder, int position, #NonNull TipsModel model) {
holder.title.setText(model.getTitle());
holder.description.setText(model.getDescription());
}
#NonNull
#Override
public TipsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,parent,false);
return new TipsViewHolder(view);
}
public class TipsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView title,description;
public TipsViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.list_title);
description = itemView.findViewById(R.id.list_desc);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "Are you working bro?", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onClick(View v) {
onListItemClick.onItemClick();
}
}
public interface OnListItemClick{
void onItemClick();
}
}
Here is the code of list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="10dp"
android:id="#+id/tipCardView"
app:cardElevation="5dp"
app:cardBackgroundColor="#E2E0EE"
app:cardCornerRadius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/list_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:background="?attr/selectableItemBackground"
android:padding="16dp">
<TextView
android:id="#+id/list_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textColor="#android:color/black"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="#+id/list_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Description" />
</LinearLayout>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/curveshape"
android:layout_gravity="end|bottom"
android:layout_marginBottom="-30dp"
android:alpha="0.2"
/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/tips"
android:layout_gravity="end|bottom"
android:layout_marginBottom="-10dp"
android:layout_marginRight="25dp"
android:alpha="0.2"
/>
</androidx.cardview.widget.CardView>
Note: I'm able to fetch data from Firestore, it is showing data properly.
Please help. I followed all other answers from Stack Overflow.
In the given setup, the OnClickListener is being set on the ViewHolder's itemView, which will be the root View in its layout, which is the CardView. However, the clickable and focusable attributes set on the LinearLayout cause it to get first grabs on touch events, so it's basically intercepting them before the CardView would handle them to respond to a click. There's no listener on the LinearLayout, though, so nothing happens.
Assuming that you want the entire item View clickable, simply remove the android:clickable="true" and android:focusable="true" attributes from the <LinearLayout>. With no clickable or focusable children, the CardView will then end up registering the click.
If instead you might want only a certain child clickable – e.g., the LinearLayout – then you would set the OnClickListener on that child, rather than the whole CardView. You still wouldn't need those attributes anywhere, though, if that's to be the only clickable child or grandchild. Those attributes usually aren't necessary in basic, relatively flat layouts, like that for your list items.
I want to create a chatapp. But in the recyclerview i only can receive the messages from the usermsg databasereference. I dont know how I should code it that i shows 2 recyclerviews. Does someone got an idea? Thanks
Chat Activity
package highelo.drivetogether;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.provider.ContactsContract;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Chat extends AppCompatActivity {
RecyclerView mFriendMsg;
RecyclerView mUserMsg;
private Button SendMsg;
private EditText EditMsg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
final Intent s = getIntent();
final String userID = s.getStringExtra("UID");
final Intent i = getIntent();
final String post_UserID = i.getStringExtra("FID");
SendMsg = (Button) findViewById(R.id.send_msg);
EditMsg = (EditText) findViewById(R.id.edit_msg);
mUserMsg = (RecyclerView) findViewById(R.id.chat_conr);
mUserMsg.setHasFixedSize(true);
mUserMsg.setLayoutManager(new LinearLayoutManager(this));
mFriendMsg = (RecyclerView) findViewById(R.id.chat_conr);
mFriendMsg.setHasFixedSize(true);
mFriendMsg.setLayoutManager(new LinearLayoutManager(this));
}
#Override
protected void onStart() {
super.onStart();
final Intent s = getIntent();
final String userID = s.getStringExtra("UID");
final Intent i = getIntent();
final String post_UserID = i.getStringExtra("FID");
DatabaseReference FriendMsg = FirebaseDatabase.getInstance().getReference().child("Users").child(userID).child("Chats").child(post_UserID).child("Chat");
DatabaseReference UserMsg = FirebaseDatabase.getInstance().getReference().child("Users").child(post_UserID).child("Chats").child(userID).child("Chat");
final FirebaseRecyclerAdapter<Blog, BlogViewHolder2> firebaseRecyclerAdapter2 = new FirebaseRecyclerAdapter<Blog, BlogViewHolder2>(
Blog.class,
R.layout.inchat_row2,
BlogViewHolder2.class,
UserMsg) {
#Override
protected void populateViewHolder(BlogViewHolder2 viewHolder, Blog model, int position) {
viewHolder.setNachricht(model.getNachricht());
viewHolder.setImage(Chat.this, model.getFImage());
}
};
mUserMsg.setAdapter(firebaseRecyclerAdapter2);
final FirebaseRecyclerAdapter<Blog, BlogViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Blog, BlogViewHolder>(
Blog.class,
R.layout.inchat_row,
BlogViewHolder.class,
FriendMsg) {
#Override
public void populateViewHolder(final BlogViewHolder viewHolder, Blog model, int position) {
viewHolder.setNachricht(model.getNachricht());
viewHolder.setImage(Chat.this, model.getFImage());
}
};
mFriendMsg.setAdapter(firebaseRecyclerAdapter);
}
public static class BlogViewHolder2 extends RecyclerView.ViewHolder{
View mView;
public BlogViewHolder2(View itemView) {
super(itemView);
mView = itemView;
}
public void setNachricht(String Nachricht){
TextView post_Nachricht = (TextView)mView.findViewById(R.id.finchat_mess);
post_Nachricht.setText(Nachricht);
}
public void setImage(Context ctx, String FImage){
ImageView post_image = (ImageView)mView.findViewById(R.id.finchat_image);
Picasso.with(ctx).load(FImage).fit().centerCrop().into(post_image);
}
}
public static class BlogViewHolder extends RecyclerView.ViewHolder{
View mView;
public BlogViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setNachricht(String Nachricht){
TextView post_Nachricht = (TextView)mView.findViewById(R.id.uinchat_mess);
post_Nachricht.setText(Nachricht);
}
public void setImage(Context ctx, String FImage){
ImageView post_image = (ImageView)mView.findViewById(R.id.uinchat_image);
Picasso.with(ctx).load(FImage).fit().centerCrop().into(post_image);
}
}
}
chat_activity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="492dp"
android:id="#+id/chat_conr"
android:layout_weight="1.11"
android:layout_alignParentTop="true"
android:layout_alignRight="#+id/send_msg"
android:layout_alignEnd="#+id/send_msg"
android:layout_above="#+id/edit_msg" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/edit_msg"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toStartOf="#+id/send_msg"
android:layout_toLeftOf="#+id/send_msg" />
<Button
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="Senden"
android:id="#+id/send_msg"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
Inchat_row
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="1dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="54dp"
android:layout_height="54dp"
android:id="#+id/uinchat_image"
app:civ_border_width="1dp"
app:civ_border_color="#ff0000"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/uinchat_mess"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:paddingLeft="5dp"
android:textSize="13dp"
android:paddingBottom="4dp"
android:singleLine="true"
android:layout_alignParentTop="true"
android:layout_toEndOf="#+id/uinchat_image"
android:layout_alignBottom="#+id/uinchat_image" />
</RelativeLayout>
</android.support.v7.widget.CardView>
Inchat2_row
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="1dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="54dp"
android:layout_height="54dp"
android:id="#+id/finchat_image"
app:civ_border_width="1dp"
app:civ_border_color="#ff0000"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/finchat_mess"
android:layout_alignParentEnd="false"
android:paddingLeft="5dp"
android:textSize="13dp"
android:paddingBottom="4dp"
android:singleLine="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="#+id/finchat_image"
android:layout_alignBottom="#+id/finchat_image" />
</RelativeLayout>
</android.support.v7.widget.CardView>
The usual way to have a chat between two people is to model that chat room in your database:
ChatRooms
ChatRoomBetweenUser1AndUser2
-KLM.......24
name: "User One"
uid: "uidOfUser1"
text: "Hello there. This is your friend User One"
-KMN.......35
name: "User Two"
uid: "uidOfUser2"
text: "Hello User One, this is User Two. Over"
That way you only need to load data from one location and need only one RecyclerView.
For more information on how to efficiently model chat rooms names for this scenario, see Best way to manage Chat channels in Firebase.
To get a good start on building a chat app, follow the Firebase Codelab for Android.
For a good introduction into modeling your data as your app needs it, see this article on NoSQL data modeling.
I have looked at a few similar incidents of this on StackOverflow. However none seem to apply to my case. The issue I'm having is that my RecyclerView is working but nothing is displaying. I have run multiple tests to try and figure out why it isn't working but all have just supported the fact that it is working correctly.
The log in getItemCount returns 3, which is the correct number. I just don't understand why it is not showing. I looked back at a recycler view I did in a previous activity and they both match to an extent(Other recycler has more information to set).
Thank you for any help you can provide.
Edit: I found the issue, but still need help. It was the collapsing toolbar that was causing it. If I move the RecyclerView outside the NestedScrollView I can see the items. However The content does not move correctly like the commented out TextViews do in the activity_project_detail.xml. I guess my new question is how do I get a RecyclerView to work inside a NestedScrollView. Thanks you!
ProjectDetailsActivity.java
package com.austine.projectpanda.activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.austine.projectpanda.R;
import com.austine.projectpanda.adapter.TaskAdapter;
import com.austine.projectpanda.data.LocalDatabase;
import com.austine.projectpanda.data.Project;
import com.austine.projectpanda.data.Task;
import java.util.ArrayList;
public class ProjectDetailsActivity extends AppCompatActivity {
private boolean connectionState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_project_details);
//Sets toolbar and up navigation
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//Gets the connection state from the intent
connectionState = getIntent().getExtras().getBoolean(LocalDatabase.CONNECTION_STATE);
//Gets the projects from the intent
Project project = getIntent().getExtras().getParcelable(LocalDatabase.PROJECT_DETAILS);
//Sets activity title
setTitle(project.getTitle());
//Generates Task arrays
ArrayList<Task> tasksUncompleted = Task.getUncompletedTasks(project.getTasks());
ArrayList<Task> tasksCompleted = Task.getCompletedTasks(project.getTasks());
//TODO: Fix recycler views
RecyclerView recyclerUncompleted = (RecyclerView) findViewById(R.id.detail_uncompleted);
recyclerUncompleted.setLayoutManager(new LinearLayoutManager(this));
recyclerUncompleted.setAdapter(new TaskAdapter(tasksUncompleted));
registerForContextMenu(recyclerUncompleted);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_projects_list, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
TaskAdapter.java
package com.austine.projectpanda.adapter;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.austine.projectpanda.R;
import com.austine.projectpanda.data.LocalDatabase;
import com.austine.projectpanda.data.Project;
import com.austine.projectpanda.data.ProjectCardView;
import com.austine.projectpanda.data.Task;
import com.austine.projectpanda.data.TaskView;
import java.util.ArrayList;
public class TaskAdapter extends RecyclerView.Adapter {
private ArrayList<Task> tasks;
public TaskAdapter(ArrayList<Task> tasks) {
this.tasks = tasks;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Selects the layout used in the Recycler
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_task, parent, false);
return new TaskView(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
TaskView view = (TaskView) holder;
//Gets the views that need to be changed
view.getTitle().setText(tasks.get(position).getTitle());
view.getSubtitle().setText(tasks.get(position).getSubtitle());
view.getCheckbox().setChecked(tasks.get(position).isCompleted());
}
#Override
public int getItemCount() {
Log.e(LocalDatabase.LOG_TAG, "GetItemCount: " + tasks.size());
return tasks.size();
}
}
TaskView.java
package com.austine.projectpanda.data;
import android.app.Activity;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import com.austine.projectpanda.R;
import com.austine.projectpanda.activity.ProjectDetailsActivity;
import com.austine.projectpanda.activity.ProjectsListActivity;
import com.austine.projectpanda.activity.WelcomeActivity;
public class TaskView extends RecyclerView.ViewHolder {
private View view;
private TextView title, subtitle;
private CheckBox checkbox;
public TaskView(View view) {
super(view);
//Gets the different parts of a layout
this.view = view;
this.title = (TextView) view.findViewById(R.id.task_title);
this.subtitle = (TextView) view.findViewById(R.id.task_subtitle);
this.checkbox = (CheckBox) view.findViewById(R.id.task_checkbox);
}
public View getView() {
return view;
}
public TextView getTitle() {
return title;
}
public TextView getSubtitle() {
return subtitle;
}
public CheckBox getCheckbox() {
return checkbox;
}
}
activity_project_details.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="192dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/toolbar_collapsing"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<ImageView
android:id="#+id/detail_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--<TextView
android:layout_width="match_parent"
android:layout_height="#dimen/detail_header_height"
android:paddingLeft="#dimen/spacing_m"
android:gravity="center_vertical"
android:text="#string/details_uncomp"
android:textSize="#dimen/text_body"
android:textColor="#color/text_subheader"/>-->
<android.support.v7.widget.RecyclerView
android:id="#+id/detail_uncompleted"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!--<TextView
android:layout_width="match_parent"
android:layout_height="#dimen/detail_header_height"
android:paddingLeft="#dimen/spacing_m"
android:gravity="center_vertical"
android:text="#string/details_comp"
android:textSize="#dimen/text_body"
android:textColor="#color/text_subheader"/>-->
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
fragment_task.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_xxl"
android:paddingLeft="#dimen/spacing_m"
android:paddingRight="#dimen/spacing_m"
android:gravity="center_vertical">
<CheckBox
android:id="#+id/task_checkbox"
android:layout_width="#dimen/spacing_l"
android:layout_height="#dimen/spacing_l" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/spacing_xl">
<TextView
android:id="#+id/task_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Set Austin on fire"
android:textSize="#dimen/text_subheading"
android:textColor="#color/text"/>
<TextView
android:id="#+id/task_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Light Austin on fire using a Zippo"
android:textSize="#dimen/text_body"
android:textColor="#color/text_subheader"/>
</LinearLayout>
</LinearLayout>
Add this property to the NestedScrollView:
android:fillViewport="true"
First, add the fillViewport property to your NestedScrollView:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:fillViewport="true">
In addition, you have to use the setAutoMesureEnabled method in the LayoutManager of the RecyclerView:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager( getActivity() );
linearLayoutManager.setOrientation( LinearLayoutManager.VERTICAL );
linearLayoutManager.setAutoMeasureEnabled( true );
The setAutoMeasureEnabled method is available since 23.2 in the support design library.
it's supper crazy but it work for me.
add one attribute android:padding="16" inside RecycelrView attribute.
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/toppers_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/large_space"
/>
I have a template layout which represents each of my rows in a ListView in my app.
Here is the photorow.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/bodylay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="#+id/leftbigsquare"
android:layout_width="0dp"
android:layout_height="140dp"
android:layout_weight="1"
android:background="#1000b0"
android:src="#drawable/test1"
/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#d0b0b0"
android:textSize="15sp" >
<ImageView
android:id="#+id/righttupperleft"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#b170b0"
android:src="#drawable/test1" />
<ImageView
android:id="#+id/rightupperright"
android:layout_width="0dp"
android:layout_height="70dp"
android:layout_weight="1"
android:background="#b110b0"
android:src="#drawable/test1" />
</LinearLayout>
<ImageView
android:id="#+id/righthorizontal"
android:layout_width="match_parent"
android:layout_height="70dp"
android:src="#drawable/test1" />
</LinearLayout>
It is repeated row by row. What I want to do is to change "ImageView" child, when I load the image from my server not redesign the row layout each time. Each row will show different "leftbigsquare", "rightupperleft", "rightupperright", "righthorizontal" (look at ImageViews ids) photos.
I researched and saw some examples, but they did that by hardcoding the layout such as :
LinearLayout A = new LinearLayout(this);
A.setOrientation(LinearLayout.HORIZONTAL);
I want to use my photorow.xml as template and just change its attributes such as source in each row.
Is it possible to use my photorow.xml as the template of a row ?
EDIT
My custom adapter :
package com.example.test2;
import java.util.List;
import com.squareup.picasso.Picasso;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class PhotoAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<PhotoRow> rowList;
Activity context = null;
public PhotoAdapter(Activity activity, List<PhotoRow> rows) {
mInflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowList = rows;
context = activity;
}
#Override
public int getCount() {
return rowList.size();
}
#Override
public Object getItem(int position) {
return rowList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView;
rowView = mInflater.inflate(R.layout.photorow, null);
//Here I get the ImageView of my photorow xml
ImageView leftBigSquare = (ImageView) rowView
.findViewById(R.id.leftbigsquare);
// Here I get the proper URL from rowList by using Picasso framework
Picasso.with(context).load(rowList.get(position).getUrls().get(0))
.resize(50, 50).centerCrop().into(leftBigSquare);
//So how can I put the leftBigSquare ImageView to my photorow template with its proper attributes
return rowView;
}
}
Yes, of course, you can use same rowview XML and still have different data in every row.
You must use a custom adapter and then inflate your row s from that adapter and based on several conditions change your data.
Paste your custom adapter code and I will show you how to do it.
In one screen in my app, I have one ListFragment that is not responding to gestures. In other screens, my ListFragments behave appropriately - moving up and down based on touch. But this instance does not move in response to gestures. I have tried setting the height to a specific dip, enclosing the ListFragment in a ScrollView but nothing works. Logs prove there are 3 entries in the list but only the first one and half of the second are visible. I can select the first entry from the list.
The xml follows. It's the inner fragment identified by " android:name="com.chex.control.LabelList"" that is not scrolling.
Has anyone encountered this problem and solved it?
Thanks
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/new_interval_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="New Interval" />
<EditText
android:id="#+id/days_in_interval"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="days in interval"
android:inputType="number"
android:singleLine="true" />
<EditText
android:id="#+id/goodi_goal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="goodi goal"
android:inputType="number"
android:singleLine="true" />
<LinearLayout
android:id="#+id/label_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<LinearLayout
android:id="#+id/default_label_pane"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Default Labels" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical" >
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/default_labels_fragment"
android:name="com.chex.control.LabelList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:focusable="true" >
</fragment>
</ScrollView>
</LinearLayout>
<LinearLayout
android:id="#+id/add_remove_label_selector"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<Button
android:id="#+id/add_label_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add" />
<Button
android:id="#+id/remove_label_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Remove" />
</LinearLayout>
<LinearLayout
android:id="#+id/label_pane"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Inteval Labels" />
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/interval_labels_fragment"
android:name="com.chex.control.IntervalLabelList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true" >
</fragment>
<EditText
android:id="#+id/new_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="new label"
android:inputType="text"
android:singleLine="true" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="#+id/keep_discard_button_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/keep_interval_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep" />
<Button
android:id="#+id/discard_interval_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Discard" />
</LinearLayout>
</LinearLayout>
</ScrollView>
here is the fragment .java
package com.chex.control;
import java.util.ArrayList;
import com.chex.R;
import com.chex.storage.Child;
import com.chex.storage.DatabaseConstants;
import com.chex.storage.DatabaseHelper;
import com.chex.storage.DatabaseIO;
import com.chex.storage.Label;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
/**
* presents all the labels on the device.
*/
public class LabelList extends ListFragment implements DatabaseConstants,
GoodiList {
private final String TAG = "LabelList";
ArrayAdapter<Label> adapter = null;
private ListParent parentActivity;
private Label[] values;
private Label currentItem;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "on Create");
ArrayList<Label> labelList = Label.getLabelsForInterval();
Log.i(TAG,labelList.size()+" labels");
// adapter = new IntervalListAdapter(getActivity(), values);
adapter = new IntervalListAdapter(getActivity(), labelList);
setListAdapter(adapter);
Log.e(TAG, "finished on Create");
}
/*
* (non-Javadoc)
*
* #see android.support.v4.app.Fragment#onActivityCreated(android.os.Bundle)
*/
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
LongClickListener longClickListener = new LongClickListener();
getListView().setOnItemLongClickListener(longClickListener);
}
/**
* create and return a CursorLoader that will take care of creating a Curso
* for the data being displayed.
*/
/**
* this used to delete. However, we need it to select usually. Now delete is
* done by long click.
*/
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// delete child - remove child from list and database
Object item = l.getItemAtPosition(position);
currentItem = (Label) item;
Log.e(TAG,
"selected " + currentItem.getLabel() + " id: "
+ currentItem.getId() + " at position " + position);
parentActivity.listItemSelected(this, position, currentItem.getId());
}
/**
* Forces cursor to requery database and list to be update. Used when a new
* child is entered in parent activity's EditText field.
*/
public void notifyDataChanged() {
Log.e(TAG, "told adapter that data changed");
adapter.notifyDataSetChanged();
}
/*
* (non-Javadoc)
*
* #see android.support.v4.app.Fragment#onAttach(android.app.Activity)
*/
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
parentActivity = (ListParent) activity;
parentActivity.setList(this);
}
class LongClickListener implements OnItemLongClickListener {
public boolean onItemLongClick(AdapterView<?> adapterView, View view,
int position, long id) {
// delete child - remove child from list and database
// adapter view. Can always call parent's getItemAtPosition
final Object item = adapterView.getItemAtPosition(position);
final Label label = (Label) item;
final String name = label.getLabel();
Log.e(TAG, "selected " + name + " at position " + position
+ " now we'll delete them");
// make sure user wants to delete
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setTitle("Delete " + name + "?");
alert.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Label.deleteLabel(name);
adapter.remove(label);
// update the list
notifyDataChanged();
}
});
alert.setNegativeButton("No",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// nothing happens - child remains
}
});
alert.show();
return true;
}
}
#Override
public void addListItem(int itemId) {
Label labelToAdd = Label.lookupLabel(itemId);
if (labelToAdd == null) {
Log.e(TAG, "didn't find a label for label id " + itemId);
} else {
Log.e(TAG, "adding label: " + labelToAdd.getLabel() + " "
+ labelToAdd.getId() + " id argument: " + itemId);
// breaks because the adapter is currently strings
adapter.add(labelToAdd);
notifyDataChanged();
}
}
#Override
public void removeListItem(int itemId) {
// TODO Auto-generated method stub
}
#Override
public void removeListItem() {
// TODO Auto-generated method stub
}
#Override
public void highlightListIndex(int index) {
ListView listView = getListView();
listView.setItemChecked(index, true);
}
}
The android is not the best with scrolling views in scrolling views, like in your case.
You have to try to avoid this situation, try to modify the layout to avoid these conflicts.
A pain in the a** method would be to intercept the parent's touch event (add an gesture listener too) and pass the event to the listfragment when the user made the scroll on the listfragment (you have to know the coordinates of the fragment inside the screen, etc). It sounds a lot easier then as is and will cause a lot of issues after that, so i recommend to rewrite your layout instead.