I don't know what I am doing wrong here.
My Adapter looks like this:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> {
private final List<String> sourceData;
public TestAdapter(List<String> sourceData) {
this.sourceData = sourceData;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.spanitem_goods, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String item = sourceData.get(position);
holder.textView.setText(item);
}
#Override
public int getItemCount() {
return sourceData.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.spanitem_imageView)
ImageView imageView;
#BindView(R.id.spanitem_textView)
TextView textView;
public ViewHolder(View v) {
super(v);
ButterKnife.bind(this, v);
}
}
}
Somewhere in my Activity I am transacting different Fragments which have all the same above adapter. It works fine with swapping them in and out, but as soon as I call this on adapter
adapter.notifyItemInserted(position);
and swap the fragments in and out again, it does not load any layout anymore. Just a blank page and this only happens when I call the above method.
My abstract BaseFragment has this in onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "... onCreateView() called");
View v = inflater.inflate(getLayoutResourceId(), container, false);
unbinder = ButterKnife.bind(this, v);
ButterKnife.setDebug(true);
return v;
}
#Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
Log.d(TAG, "... onDestroyView() called");
}
Well I found the issue. Seems pretty unrelated to the entry question, but I might help someone else.
I was using the Observer pattern and had the update method in a non-ui thread running. Seems like that was the problem, since the RecyclerView never got updated unless I scrolled manually.
#Override
public void update(final String epc) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
sourceData.add(epc);
notifyDataSourceChanged(sourceData.size() - 1);
}
});
}
Related
I've got a RecyclerView that needs to update in real-time when content is received. It successfully shows all the content at first load. When attempting to add a new item dynamically, it makes no difference. Is there anything I'm doing wrong here? Much appreciated!
MainActivity:
RecyclerView rvClips;
ClipboardRVAdapter adapter;
ArrayList<Clip> clips = new ArrayList<>();
private void SetupRV() {
adapter = new ClipboardRVAdapter(clips);
rvClips.setAdapter(adapter);
adapter.setClickListener(this);
rvClips.setLayoutManager(new LinearLayoutManager(getActivity()));
}
//this method gets called everytime a new item is received. I confirmed that it gets called.
public void OnNewClipReceived(Clip clip) {
clips.add(0, clip);
adapter.notifyItemInserted(0);
}
Adapter:
Standard adapter code. Simply shows all the items in the recyclerview.
public class ClipboardRVAdapter extends RecyclerView.Adapter<ClipboardRVAdapter.ViewHolder> {
private ItemClickListener mClickListener;
ArrayList<Clip> clips;
public ClipboardRVAdapter(ArrayList<Clip> clips) {
this.clips = clips;
}
public void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Inflate the custom layout
View contactView = inflater.inflate(R.layout.item_clipboard_clip, parent, false);
// Return a new holder instance
ViewHolder viewHolder = new ViewHolder(contactView);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Clip clip = clips.get(position);
holder.tvClipTitle.setText(clip.content);
}
#Override
public int getItemCount() {
return clips.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView tvClipTitle;
public ViewHolder(View itemView) {
super(itemView);
tvClipTitle = itemView.findViewById(R.id.tvClipTitle);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
}
Since you mentioned 'realtime', I'm guessing this could be a thread issue. Your code seems correct. It could be that the adapter needs to be refreshed in a runOnUiThread function. Try this:
public void OnNewClipReceived(Clip clip) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
clips.add(0, clip);
adapter.notifyItemInserted(0);
}
});
}
You would need this method in your RecyclerView adapter.
void addNewClip(Clip clip, int index) {
this.clips.add(index, clip);
notifyItemInserted(index);
}
Adding to clips(MainActivity's) won't make a difference because your adapter has it own ArrayList to work with.
I am using navigation component in my app I have 2 fragments one fragments list of items and another shows detail of an item when user clicks on an item in fragments 1 it goes to detail fragment and when I switch back to first fragment then all the listing duplicates again.
Below is my code:
CakeFragment.java
public class CakeFragment extends Fragment {
List<AllCakes> allCakeList = new ArrayList<>();
AllCakesAdapter adapter;
BottomNavigationView navView;
FragmentCakeBinding fragmentCakeBinding;
public CakeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeBinding = FragmentCakeBinding.inflate(inflater,container,false);
navView = getActivity().findViewById(R.id.navView);
navView.setVisibility(View.GONE);
getAllCakes();
return fragmentCakeBinding.getRoot();
}
private void getAllCakes(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<AllCakes>> call = apiService.getAllCake();
call.enqueue(new Callback<List<AllCakes>>() {
#Override
public void onResponse(Call<List<AllCakes>> call, Response<List<AllCakes>> response) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
fragmentCakeBinding.allCakeRecycler.setHasFixedSize(true);
fragmentCakeBinding.allCakeRecycler.setLayoutManager(new LinearLayoutManager(getActivity()));
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
#Override
public void onFailure(Call<List<AllCakes>> call, Throwable t) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
TastyToast.makeText(getActivity(),t.getMessage(),TastyToast.LENGTH_SHORT,TastyToast.ERROR).show();
}
});
}
}
AllCakesAdapter.java
public class AllCakesAdapter extends RecyclerView.Adapter<AllCakesAdapter.ViewHolder> {
Context context;
List<AllCakes> allCakeList;
public AllCakesAdapter(Context context, List<AllCakes> allCakeList) {
this.context = context;
this.allCakeList = allCakeList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.allcakes_row,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
AllCakes model = allCakeList.get(position);
Glide.with(context).load(model.getImgurl()).into(holder.allCakeImg);
holder.allCakeName.setText(model.getName());
holder.cakeDisPrice.setPaintFlags(holder.cakeDisPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.moreCake.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavController navController = Navigation.findNavController((Activity) context,R.id.fragment);
navController.navigate(R.id.cakeDetailFragment);
}
});
}
#Override
public int getItemCount() {
return allCakeList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView allCakeImg;
Button moreCake;
TextView allCakeName;
public ViewHolder(#NonNull View itemView) {
super(itemView);
allCakeImg = itemView.findViewById(R.id.allCakeImg);
moreCake = itemView.findViewById(R.id.moreCake);
allCakeName = itemView.findViewById(R.id.allCakeName);
}
}
}
CakeDetailFragment.java
public class CakeDetailFragment extends Fragment {
FragmentCakeDetailBinding fragmentCakeDetailBinding;
public CakeDetailFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeDetailBinding = FragmentCakeDetailBinding.inflate(inflater,container,false);
return fragmentCakeDetailBinding.getRoot();
}
}
Why is this happening?
private void getAllCakes(){
...
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
...
you're calling:
allCakeList.addAll(response.body());
every time without clearing your list out.
you have to clear that list:
allCakeList.clear();
allCakeList.addAll(response.body());
this is something you can easily determine yourself by just putting a breakpoint on your allCakeList to see what's inside it, if you haven't ever done this before, you should consider trying it out
Try the following and see if it solves your issue
CakeFragment.java
allCakeList.clear();
allCakeList.addAll(response.body());
//rest of the logic remains same
I want to use admob with recyclerview but there is a problem. I need to hide some elements that are belong to viewholder. I need to hide the imageview in which position the ImageView belongs. When i click holder.btnReklamIzle the picture in that position must be hid in onRewardedVideoAdLoaded method. How can i transmit the position to onRewardedVideoAdLoaded method?
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_kuponlar, container, false);
mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(KuponlarFragment.this.getActivity());
mRewardedVideoAd.setRewardedVideoAdListener(this);
tahminlerRecyclerView = root.findViewById(R.id.tahminlerRecyclerView);
linearLayoutManager = new LinearLayoutManager(this.getActivity());
tahminlerRecyclerView.setLayoutManager(linearLayoutManager);
tahminlerRecyclerView.setHasFixedSize(true);
loadRewardedVideoAd();
fetch();
return root;
}
private void loadRewardedVideoAd() {
mRewardedVideoAd.loadAd(getString(R.string.admob_ads_id),
new AdRequest.Builder().build());
}
private void fetch() {
Query query = FirebaseDatabase.getInstance()
.getReference()
.child("tahminler");
FirebaseRecyclerOptions<Mac> options =
new FirebaseRecyclerOptions.Builder<Mac>()
.setQuery(query, new SnapshotParser<Mac>() {
#NonNull
#Override
public Mac parseSnapshot(#NonNull DataSnapshot snapshot) {
return new Mac((double)snapshot.child("oran").getValue(),
snapshot.child("tahmin").getValue().toString(),
);
}
})
.build();
adapter = new FirebaseRecyclerAdapter<Mac, ViewHolder>(options) {
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.tahmin_tasarim_recyclerview, parent, false);
return new ViewHolder(view);
}
#Override
protected void onBindViewHolder(final ViewHolder holder, final int position, Mac mac) {
holder.btnReklamIzle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mRewardedVideoAd.isLoaded()) {
mRewardedVideoAd.show();
}
}});
}
};
tahminlerRecyclerView.setAdapter(adapter);
}
#Override
public void onRewardedVideoAdLoaded() {
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView image;
public ViewHolder(View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
}
}
onRewardedVideoAdLoaded is a callback method for an asynchronous operation hence you cannot pass values to it as arguments but use referenced variables.
For your case do the following:
Firstly
Create a global variable to hold the list of views to hide
ArrayList<View> views_to_hide = new ArrayList<>();
Secondly
Create a helper function to hide the views
function hideViews(ArrayList<View> views){
for(View v : views) v.setVisibility(View.GONE);
}
Thirdly
Inside onBindViewHolder Add to the list the views you want to hide under your button onClick
public void onClick(View v) {
//...
if (mRewardedVideoAd.isLoaded()) {
mRewardedVideoAd.show();
// Ads already shown you may want to manually hide other images here
}else{
// We only need to add to list when ads not loaded
// We also want to make sure we don't add same view to the list twice
if(!views_to_hide.contains(holder.image))
views_to_hide.add(holder.image);
}});
//...
Finally
Call your helper function inside onRewardedVideoAdLoaded
#Override
public void onRewardedVideoAdLoaded() {
//This hides the views that was added to the list before now
hideViews(views_to_hide);
}
I want to use a recyclerView to show some products, they are inside firebase-firestore and, to get them I use a query. After this, I call the adapter to show but nothing appears.
You can see my adapter where:
public class ProdutoRecyclerAdapter extends FirestoreRecyclerAdapter <Produto, ProdutoRecyclerAdapter.ProdutoHolder> {
public ProdutoRecyclerAdapter(#NonNull FirestoreRecyclerOptions<Produto> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ProdutoHolder produtoHolder, int i, #NonNull Produto produto) {
produtoHolder.tNome.setText(produto.getNomeP());
produtoHolder.tPontos.setText(String.valueOf(produto.getPontosP()));
}
#NonNull
#Override
public ProdutoHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.lista_produtos,parent,false);
ProdutoHolder holder = new ProdutoHolder(v);
return holder;
}
class ProdutoHolder extends RecyclerView.ViewHolder{
TextView tNome;
TextView tPontos;
public ProdutoHolder(#NonNull View itemView) {
super(itemView);
tNome = itemView.findViewById(R.id.nomeP);
tPontos= itemView.findViewById(R.id.pontosP);
}
}
And here there is my fragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.store,null);
toolbar = view.findViewById(R.id.toolbar_loja);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
Query query = produtoRef.orderBy("pontos");
FirestoreRecyclerOptions<Produto> option = new FirestoreRecyclerOptions.Builder<Produto>()
.setQuery(query, Produto.class)
.build();
adapter = new ProdutoRecyclerAdapter(option);
recyclerView = (RecyclerView) view.findViewById(R.id.produtos_list);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
I don't have any error, but nothing appears on the screen, and I already did the debug and I know the variables are passing the right value.
At your adapter, you should have the override of getItemCount() and getItem()
/**
* Gets the item at the specified position from the backing snapshot array.
*
* #see ObservableSnapshotArray#get(int)
*/
#NonNull
public T getItem(int position) {
return mSnapshots.get(position);
}
#Override
public int getItemCount() {
return mSnapshots.isListening(this) ? mSnapshots.size() : 0;
}
where mSnapshots is your array of data.
Docuementation here
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have an activity that has container that contain fragments and this fragment has other fragments.
Now I want this second or child fragment to access views in main activity, but it returns null pointer exception.
class:
public class ImageListFragment extends AbsListViewBaseFragment implements ObservableScrollViewCallbacks {
public static final int INDEX = 0;
android.support.design.widget.FloatingActionButton mFab;
#Bind(R.id.ic_call)
ImageView mIcCall;
#Bind(R.id.ic_email)
ImageView mIcEmail;
#Bind(R.id.ic_forum)
ImageView mIcForum;
FabToolbar mFabToolbar;
ObservableListView mObservableListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fr_image_list, container, false);
listView = (ListView) rootView.findViewById(android.R.id.list);
((ListView) listView).setAdapter(new ImageAdapter(getActivity()));
final SubTaB mainActivity = (SubTaB)getActivity();
ButterKnife.bind(mainActivity);
//////////////// problem here
mFabToolbar = (FabToolbar) rootView.findViewById(R.id.fabtoolbar);
////////////////
getFragmentManager().findFragmentByTag("TAG");
// rootView.findViewById(R.id.fab);
mObservableListView = (ObservableListView)rootView.findViewById(android.R.id.list);
//
mObservableListView.setAdapter(this.listView.getAdapter());
mObservableListView.setScrollViewCallbacks(this);
mainActivity.mFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mainActivity.getApplicationContext(), "msg msg", Toast.LENGTH_LONG).show();
mainActivity.mFabToolbar.expandFab();
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
startImagePagerActivity(position);
}
});
return rootView;
}
#Override
public void onDestroy() {
super.onDestroy();
AnimateFirstDisplayListener.displayedImages.clear();
}
private static class ImageAdapter extends BaseAdapter {
private static final String[] IMAGE_URLS = Constants.IMAGES;
private LayoutInflater inflater;
private ImageLoadingListener animateFirstListener = new AnimateFirstDisplayListener();
private DisplayImageOptions options;
ImageAdapter(Context context) {
inflater = LayoutInflater.from(context);
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // تغيير الفيو قبل تحميل الصورة
.showImageForEmptyUri(R.drawable.ic_empty) // لما الصورة فاضية
.showImageOnFail(R.drawable.ic_error) // عند الفشل
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.displayer(new CircleBitmapDisplayer(Color.WHITE, 5))
.build();
}
#Override
public int getCount() {
return IMAGE_URLS.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
final ViewHolder holder;
if (convertView == null) {
view = inflater.inflate(R.layout.item_list_image, parent, false);
holder = new ViewHolder();
holder.text = (TextView) view.findViewById(R.id.text);
holder.image = (ImageView) view.findViewById(R.id.image);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
holder.text.setText("Item " + (position + 1));
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], holder.image, options, animateFirstListener);
return view;
}
}
static class ViewHolder {
TextView text;
ImageView image;
}
#Override
public void onScrollChanged(int i, boolean b, boolean b1) {
}
#Override
public void onDownMotionEvent() {
}
#Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
Log.d("","Scroll scroll scroll");
if (scrollState == ScrollState.UP) {
mFabToolbar.slideOutFab();
} else if (scrollState == ScrollState.DOWN) {
mFabToolbar.slideInFab();
}
}
#OnClick(R.id.fab)
void onFabClick() {
mFabToolbar.expandFab();
}
#OnClick(R.id.call)
void onClickCall() {
iconAnim(mIcCall);
}
#OnClick(R.id.ic_email)
void onClickEmail() {
iconAnim(mIcEmail);
}
#OnClick(R.id.ic_forum)
void onClickForum() {
iconAnim(mIcForum);
}
private void iconAnim(View icon) {
Animator iconAnim = ObjectAnimator.ofPropertyValuesHolder(
icon,
PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f, 1f),
PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f, 1f));
iconAnim.start();
}
private static class AnimateFirstDisplayListener extends SimpleImageLoadingListener {
static final List<String> displayedImages = Collections.synchronizedList(new LinkedList<String>());
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
if (loadedImage != null) {
ImageView imageView = (ImageView) view;
boolean firstDisplay = !displayedImages.contains(imageUri);
if (firstDisplay) {
FadeInBitmapDisplayer.animate(imageView, 500);
displayedImages.add(imageUri);
}
}
}
}
}
It is not good practise to find and control a view in this way. Views can easily become detached from activities and cause unexpected exceptions.
You should rather look at using callbacks to communicate between fragments and activities if required. That way, it also keeps your code in the correct places - so the activity is the only one touching its own views and the fragment also only touches its own views. It merely tells the activity (via callbacks) that something has happened that the activity might want to know about. It also ensures that the fragments are completely self contained and can be easily reused.
You can read about how to implement callbacks here: http://developer.android.com/training/basics/fragments/communicating.html
Use EventBus to communicate between the activity and the fragment. riggarro suggestion is the correct way. But you can also able to update the base activity views using the EventBus.
For example we need to update a TextView text in a activity from the fragment, follow the steps.
First you need to add the following library as dependency to your project in build.gradle of your app.
compile 'de.greenrobot:eventbus:2.4.0'
First you need to create a Event Object class to communicate between the fragment and activity like below.
public class UpdateTextEvent {
private String sampleTextValue;
public UpdateTextEvent(String textValue) {
this.sampleTextValue = textValue;
}
public String getTextValue() {
return sampleTextValue;
}
}
You need to post a event to the event bus in the fragment to update the TextView in the activity.
public class TestingFragment extends Fragment{
private EventBus bus = EventBus.getDefault()
public TextingFragment(){}
public void onCreate(Bundle onSavedInstanceState){
super.onCreate(onSavedInstanceState);
}
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.sample_activity, parent, false);
...
Button b1 = (Button) v.findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//trigger a update to the activity
bus.post(new UpdateTextEvent("testing"));
}
});
}
}
After that you need to register the bus with the callback of the event in the activity like below.
public class MainActivity extends Activity{
private EventBus bus = EventBus.getDefault();
private TextView textView;
#Override
public void onCreate(Bundle onSavedInstanceState){
super.onCreate(onSavedInstanceState);
....
// The textview going to be updated on posting the event
textView = (TextView) findViewById(R.id.text1);
bus.register(this);
}
public void onEvent(UpdateTextEvent event){
textView.setText(event.getTextValue());
}
}
In this above example the onEvent method will be called when you post a event from the fragment..
Hope it will help you.