I'm an android developer and I've got this issue regarding some OOP concepts in android. Whenever we create an adapter (lets take a recycler adapter ..) I usually do something like this:
public class MyAdapter extends RecyclerView.Adapter<"MyAdapter.MyViewHolder">
{
public MyAdapter(){}
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){}
public void onBindViewHolder(MyViewHolder holder, int position){}
public class MyViewHolder extends RecyclerView.ViewHolder{}
My question is in case we have our MyViewHolder as an internal class in our adapter (like written above....), why does it have to be declared as public/protected ?? Anytime I try to make the class private,I get an error at the top of the adapter class: 'packagename.MyAdapter.MyViewHolder has a private access in packagename.MyAdapter'
Ever since i've been looking at tutorials (text or video) which explain how adapters work, they have never tried to explain why the MyViewHolder class needs to be declared as public. I really wanna understand this stuff.
Related
I'm trying to insert a private class into brackets (Generics) of outer class declaration and getting the next error : "com.example.gridrecyclerview.RecyclerViewAdapter.VieHolder has private access in com.example.gridrecyclerview.RecyclerViewAdapter"
When changing inner class to public everything ok. Can someone explain me the issue.
Attached code:
package com.example.gridrecyclerview;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import java.util.Random;
public class RecyclerViewAdapter extends RecyclerView.Adapter <RecyclerViewAdapter.ViewHolder> {
Context context;
List<ModelItem> modelItemList;
public RecyclerViewAdapter(Context context, List<ModelItem> modelItemList)
{
this.context = context;
this.modelItemList = modelItemList;
}
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(context).inflate(R.layout.rc_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position)
{
Log.e("onBindViewHolder ","onBindViewHolder");
ModelItem modelItem = modelItemList.get(position);
holder.firstNameTextView.setText(modelItem.getFirstName());
holder.secondNameText.setText(modelItem.getLastName());
}
#Override
public int getItemCount()
{
return modelItemList.size();
}
private class ViewHolder extends RecyclerView.ViewHolder
{
TextView firstNameTextView;
TextView secondNameText;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
firstNameTextView = itemView.findViewById(R.id.first_name);
secondNameText = itemView.findViewById(R.id.second_name);
}
}
}
To answer this question you should know what happened behind the scene when you use generics in Java, Generics are checked at compile time and there is no generic type at runtime this is called type erasure, when you compile your program all generics are castes to their specific types. Imagine your RecyclerView.Adapter class is something as follow:
public class Adapter<T> {
T view;
public T getView() {
return view;
}
public void setView(T view) {
this.view = view;
}
}
And Imagine you have defined your RecyclerViewAdapter class as before:
Now lets discuss this problem from two points of view:
Programmer
Compiler
As a programmer imagine somewhere in another class you create an instance of RecyclerViewAdapter class as follow and call getView() on that object:
RecyclerViewAdapter recycler = RecyclerViewAdapter();
? object = recycler.getView()
What type of object you put here instead of ?
clearly you can't put RecyclerViewAdapter.ViewHolder because it is private for RecyclerViewAdapter and you can't use it elsewhere.
Now lets consider this problem from compiler point of view:
When compiling generics, compiler use erasure and compile your code to something like this:
RecyclerViewAdapter recycler = RecyclerViewAdapter();
? object = (RecyclerViewAdapter.ViewHolder)recycler.getView();
Here you can see compiler cast the type of your object at compile time((RecyclerViewAdapter.ViewHolder)recycler.getView()). But is it possible for compiler to do something like this here?
Of course not because RecyclerViewAdapter.ViewHolder is private for RecyclerViewAdapter class while this code is written in another class.
You can not do this, because there you are passing the private class to "RecyclerView.Adapter" class. In other words, the ViewHolder class only available inside the RecyclerViewAdapter class.
PS: You can create this class as a new Class, then pass it through "RecyclerView.Adapter" class or change it to public scope.
As per the accessibility rule in Java-Spec Page -164, a private member declared is accessible only within the body of the top level class. I hope body of the class means not the definition of it.
**
Otherwise, the member or constructor is declared private, and access is permitted if and only if it occurs within the body of the top level class (ยง7.6) that encloses the declaration of the member or constructor.
**
Can I have adapter that uses singleton instance as a data source? I am not going to have any static references to Views/Context, only plain data stored in singleton. Will there be any leaks or any downsides of such solution?
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/* SINGLETON */
private DataManager manager;
public MyAdapter(DataManager manager) {
this.manager = manager;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create vh
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// bind vh
}
#Override
public int getItemCount() {
return manager.itemsCount();
}
}
DataManager is a singleton object that holds state/data in my Service.
Thanks in advance.
Your adapter referencing a singleton doesn't leak anything. Your data manager can leak memory if it's not implemented correctly.
Using singletons to store static data is just fine. You can just dispose the data when you're done using it.
There are two cases that you should use or not.
If you'r getting data from Server and pass to Adapter then you should uses Singleton because it is a good idea for holding data instead of calling WebService every-time.
If your data is static then you need not to hold the data in Singleton because it will slow down your app.
As it's a singleton, this means the class holds a reference to an instance of the class, and therefore the data will always be held in memory. This could cause your app to become less responsive if it is holding lots of resources.
In addition, if your app has a large footprint, Android's OS will be more likely to destroy your app in the event of low memory.
If possible (and it usually is), avoid singletons.
I have an abstract adapter class from an external library:
public abstract class DragItemAdapter<T, VH extends DragItemAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
//Their other codes
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(final View itemView, int handleResId) {
super(itemView);
//The rest of their codes
}
}
}
And I have my Adapter extended that adapter
public class ChecklistAdapter extends DragItemAdapter<Pair<Integer, SomeClass>, ViewHolderForChecklist> {
#Override
public ViewHolderForChecklist onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
grab = R.id.grab;
return new ViewHolderForChecklist(view,grab);
}
}
If my ViewHolderForChecklist is an inner class of the ChecklistAdapter it works fine. But if I move the ViewHolderForChecklist to a brand new class
public class ViewHolderForChecklist extends DragItemAdapter<Pair<Long, SomeClass>, ViewHolderForChecklist>.ViewHolder { // The error is at this line
public ViewHolderForChecklist(final View itemView, int grab) {
super(itemView, grab);
}
#Override
public void onItemClicked(View view) {
}
#Override
public boolean onItemLongClicked(View view) {
return true;
}
}
There is an error in real time
No enclosing instance of type 'library.package.name.DragItemAdapter' class is in scope
and the error when compile
error: an enclosing instance that contains DragItemAdapter.ViewHolder is required
Using "move" from Refractor has the same problem. I'm still new to this kind of... 'nested-class" so I don't know what is wrong with this or what kind of info should I include more.
Thank you!
ViewHolder is an inner class of DragItemAdapter (because it wasn't declared static). That means that every object of class ViewHolder must be associated with an object of class DragItemAdapter (actually, it would have to be a subclass of DragItemAdapter). You can think of ViewHolder having a hidden instance variable like
DragItemAdapter __outerObject;
The ViewHolder can directly access instance variables and methods belonging to the __outerObject.
That means that when you say new ViewHolder(...), you have to have some DragItemAdapter for the ViewHolder to be associated with.
The same applies to any subclass of ViewHolder, including ViewHolderChecklist, since the subclass inherits the hidden __outerObject variable.
In the first example, where ViewHolderChecklist is inside a ChecklistAdapter, the onCreateViewHolder method will always be called on a ChecklistAdapter instance. When that method says new ViewHolderChecklist, the new object's __outerObject will be set to the ChecklistAdapter instance. Also, if an outside class has a ChecklistAdapter adapter;, it can use that to create a new ViewHolderChecklist by saying adapter.new ViewHolderChecklist(...).
When you move ViewHolderChecklist outside the class, though, there's no way for a new instance to be created, since there's no way to use new in a way that would tell it what its __outerObject is supposed to be. The adapter.new ViewHolderChecklist(...) syntax won't work, because that syntax is only allowed for nested classes, and ViewHolderChecklist isn't a nested class. So ViewHolderChecklist has to be a nested class inside a subclass of DragItemAdapter.
Correction: It's actually possible to declare ViewHolderChecklist like this. However, you have to give it an explicit constructor and it has to have a Qualified Superclass Constructor Invocation (see this; see also https://stackoverflow.com/questions/40501642/what-rule-prohibits-this-nested-class-reference/40501815.
I searched the internet and read out documents on Google Android Help Centre, But still now I am not clear what the difference between the two and when I will use it at what situation?
I go through stack-overflow not found any detailed answer.
serviceListViewProviderPage.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//something to do
}
});
and
serviceListViewProviderPage.setOnItemClickListener(this);
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//something to do
}
Thanks in Advance
Both are same but different declarations and uses. First, lets see what we are doing.
Here:
view.setOnItemClickListener(Listener);
You are setting a listener in your view.
After, you must override the method onItemClick of the OnItemClickListener interface in order to follow the contract provided and make an action on item click.
Now see your code examples:
FIRST CASE
// set a listener to your wiew
serviceListViewProviderPage.setOnItemClickListener(
// create a new OnItemClickListener
new AdapterView.OnItemClickListener() {
#Override
//
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//something to do
}
});
Here you're declaring the listener as a anonymous inner class at time you set it to your view.
PROS:
fast to code
CONS:
if the logic inside the metod is too long or the interface has many methods you will loose readability
you cannot reuse the logic inside the Listener
can cause memory leaks (thanks to #Murat K)
SECOND CASE
To understand second one you must see the code MUST be inside a View that implements AdapterView.OnItemClickListener, that's why you can use this
// here you set the class itself as a listener
serviceListViewProviderPage.setOnItemClickListener(this);
But, as long as you must follow the contract of the interface, the class must implement the method:
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//something to do
}
PROS:
readability
reusability
CONS:
make a View be also a Listener is not my prefered way, I like more to have a class that is only a Listener and another is only a View.
OnItemClickListener is an interface and interface has a property that the class which implements it must implement its unimplemented method(s) (because interface contains declaration of methods not implementation).
"serviceListViewProviderPage.setOnItemClickListener(this);"
In above statement you need to implement OnItemClickListener in your class and its unimplemented methods and providing your class reference as this reference as argument.
new AdapterView.OnItemClickListener()
In above statement you are using anonymous class.
To better understand you should read interface, anonymous class concept, this reference.
on the first case, you decide that your interface OnItemClickListener should be used as an anonymous implementation (meaning that you provide the code right then and there, for one time use in your project).
so in this case you define your interface implementation right when you call the method:
serviceListViewProviderPage.setOnItemClickListener(new AdapterView.OnItemClickListener() {....});
In the second case is even more simple:
Instead of writing all the code for one time anonymous use, you make the class containing the code implement the interface you want ( in this case the AdapterView.OnItemClickListener interface)
Therefore when you call the method you pass "this" as a parameter, because your class is implementing the interface required by the method
This is shown by the fact that you have the method defined directly in the class:
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//something to do
}
and because of that your class implements the interface and you can call:
serviceListViewProviderPage.setOnItemClickListener(this);
When and where to use each of these forms will be at the sofware developer's discretion, after evaluating how to better follow and use the best practice guidelines for software development
Hope this helps!
I have a fragment lets call it EventFragment, and I have an RecyclerView.Adapter called EventAdapter, inside that adapter is a viewholder class called EventViewHolder. I want access an animation method inside the viewholder class from the Fragment.
How would I accomplish this? I was thinking to define another interface to accomplish this.
This is what I have so far:
OnAnimationListener
public interface OnAnimationListener {
void onAnimation();
}
Adapter
public EventAdapter extends RecyclerView.Adapter<RecycleView.ViewHolder> {
// Boilerplate initialization stuff here
public class EventViewHolder extends RecyclerView.ViewHolder implements OnAnimationListener {
// Initialization code
#Override
public void onAnimation() {
// Do an animation
}
}
}
EventFragment
public class EventFragment extends Fragment {
// Boilerplate initialization code
}
I am thinking I should implement an interface inside the Fragment like so:
public class EventFragment extends Fragment implements onAnimationListener {
private void initAdapter() {
mAdapter.setOnAnimationListener(this);
}
#Override
public void onAnimation(Data data) {
// pass any data
}
}
Then I'm sort of stuck at this point. Normally you would call this once the listener is passed through:
OnAnimationListener.onAnimation(...)
But this doesn't make sense. The code flow goes like this:
EventFragment --> Adapter --> ViewHolder
I need to implement the following code flow:
EventFragment (get access to specific viewholder and do animation) <--> Viewholder
How should I accomplish this? Maybe pass the interface from the Viewholder to the Fragment instead, and call mOnAnimationListener.onAnimation() from the fragment right?
I want to have specific control of when the animation occurs on the EventViewHolder and I want to have this control from the EventFragment how should I go about tackling this?
If you simply want to animate the RecyclerView's items you can take a look at RecyclerView.ItemAnimator.
If not (or if you still want to handle this in the fragment) you can attach click listeners for your views inside the ViewHolder object and handle the clicks by passing the event up ViewHolder->Adapter->Fragment via the mechanism you already have (and passing the view received in onClick as a parameter). Personally I'd stay away from this pattern. One reason being that the RecyclerView can be scrolled in the meantime and I don't have enough knowledge right now on what happens to that particular view once it's off position or maybe even off screen. Or maybe pointing to other data.