I am trying to implement something very common for web developers with android view groups and annotations.
The idea is to have basic ViewGroup that has its own layout basic. Like header, content and footer.
All other view groups that have the same layout,but different content inside blocks have to extend this base ViewGroup.
For example my BaseViewGroup
#EViewGroup(R.layout.base_view_group)
public abstract class BaseViewGroup extends RelativeLayout {
private final LayoutInflater mLayoutInflater;
#ViewById
public RelativeLayout rlHeader;
#ViewById
public RelativeLayout rlFooter;
#ViewById
public RelativeLayout rlScrollMain;
// Template methods for inflating dialog
public abstract int getHeaderViewId();
public abstract int getFooterViewId();
public abstract int getScrollMainViewId();
public CDialogBase(DialogChain dialogChain, Context context) {
super(context);
mLayoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#AfterViews
void afterViews() {
rlHeader.addView(mLayoutInflater.inflate(getHeaderViewId(), rlHeader, false));
rlScrollMain.addView(mLayoutInflater.inflate(getScrollMainViewId(), rlHeader, false));
rlFooter.addView(mLayoutInflater.inflate(getFooterViewId(), rlFooter, false));
}
}
And when we need concreate class we have to extend and implement template methods.
// #EViewGroup
public class ConcreteViewGroup extends BaseViewGroup {
#ViewById
TextView textView;
#ViewById
Button button;
#Override
public int getHeaderViewId() {
return R.layout.concrete_header;
}
#Override
public int getFooterViewId() {
return R.layout.concrete_footer;
}
#Override
public int getScrollMainViewId() {
return R.layout.concrete_main;
}
#AfterViews
#Click
#Click
......
}
So I need to extend base ViewGroup and provide concreate resources in template methods. And than in inherited concrete class also use annotations for finding view and other stuff.
I have got an error because my inherited class is not annotated, but if annotate it, it will crash because I have to provide layout in annotation.
Is it possible to have something like I've described above ?
Thanks everyone in advance.
You have to specify the layout in the subclass as well : #EViewGroup(R.layout.base_view_group).
Related
My app has more than 4 lists of different data models.
I want to create a more generic CommonAdapter that extends PagedListAdapter
Here is my current code
public abstract class CommonPagedListAdapter<T, VH extends RecyclerView.ViewHolder>
extends PagedListAdapter<T, VH> {
private Context mContext;
private ArrayList<T> mArrayList;
public abstract void onItemClick(T model, int position);
public abstract int getLayoutResId();
protected CommonPagedListAdapter(Context context, ArrayList<T> arrayList,
#NonNull DiffUtil.ItemCallback<T> diffCallback) {
super(diffCallback);
this.mContext = context;
this.mArrayList = arrayList;
}
#NonNull
#Override
public VH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//what should i return here?
View view = LayoutInflater.from(mContext).inflate(getLayoutResId(),parent,false);
return (VH) new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull VH holder, int position) {
//what should i do here?
// holder
}
class ItemViewHolder extends RecyclerView.ViewHolder {
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
I'm using PagelListAdapter from the Android Paging Library
I would like to know a few things:
- What should be set in the onCreateViewHolder as I'll be having different ViewHolders?
- What should be set in onBindViewHolder?
- Is this really the right way that makes the CommonPagedListAdapter extensible and maintainable?
I had a similar issue where I tried to create a single adapter to use for multiple different list types. I ultimately came to the conclusion that it is best to use a separate adapter for each list type solely because it avoids having to make a very big "common" adapter class which goes against the "single-responsibility" principle. That way, each adapter is a lot smaller, more maintainable, and flexible.
However, if you really want to use a single adapter for similar items, the way I usually do it is by creating unique ViewHolders for each item type and then binding them accordingly in onBindViewHolder using a switch statement or something similar. In order to do this, you would need to override an additional method called getItemViewType in your adapter.
There's a really good guide that goes over how to create an adapter that can handle different view types on Codepath: https://guides.codepath.com/android/Heterogenous-Layouts-inside-RecyclerView
Now,I want to replace all view by my own AppCompatDelegate.
And override method createView so that I can update all the views in project.
Now I can do update like this:
public class UpdatedView extends View
in AppCompatDelegate :
public View createView(String name) {
if ("View".equals(name)) {
return UpdatedView();
}
}
Now, the problem I'm facing is that is it possible to create a view wrapper for all View. So that I can write like this
public class ViewWrapper extends View{
View realView;
#Override
public void methods() {
//override the method which is need, like ondraw, onClick
doUpdateThings();
realView.methods();
}
}
wrapper all views like this.
public View createView(String name) {
if ("View".equals(name)) {
return UpdatedViewWrapper(new View());
}
}
I want to know whether it's possible or not, and safe or not.
Someting added: it seems, I don't need the replace delegate but override onCreateView is enough.
I want to create an interface between an adapter and a view holder (this view holder is an inner class of another adapter) so that I can update the text view (number). How can I do this?
In detail:
I have two recycle views (Main List Recycler View and Sub List Recycler View horizontally placed as shown in the fig) one having a number (as one of its item) and other having checkbox (as its item).
I have two adapters FilterMainListAdapter and FilterSubListAdapter with view holders FilterMainListViewHolder and FilterSubListViewHolder populating the fields.
When checkboxes are selected in the Sub List Recycler View, I want the corresponding number in the Main List Recycler View to update.
For this, I'm using and Interface.
public interface ChangeFilterMainNumber {
void OnChangeFilterMainNumberListener(int totalCheckedNumber);
}
I've checkbox's onClick method inside the FilterSubListViewHolder and I'm trying to send the total check boxes checked number as follows.
changeFilterMainNumber.OnChangeFilterMainNumberListener(totalCheckedNumber);
After that, I'm implementing ChangeFilterMainNumber interface inside the FilterMainListViewHolder
public class FilterMainListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
ChangeFilterMainNumber {...}
But How can I define this interface inside the FilterSubListAdapter?
changeFilterMainNumber = ???;
[If it is an activity one can define the interface like this changeFilterMainNumber = (ChangeFilterMainNumber) context inside the default constructor of FilterSubListAdapter. But what about a view holder that is an inner class of another adapter?]
or is there a better approach in finding a solution to my problem other than this?
Update: You can take a look at the code here https://github.com/gSrikar/FilterScreen
If I implement the function as you want, I will implement like this:
(This is like an Observer pattern)
class Fragment/Activity implement OnChangeFilterMainNumberListener{
FilterMainListAdapter mainAdapter;
FilterSubListAdapter subAdapter;
void oncreate() {
mainAdapter = new FilterMainListAdapter(this);
}
#Override
void OnChangeFilterMainNumberListener(int totalCheckedNumber) {
.....
// Update data to sub list
}
}
class FilterMainListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public interface ChangeFilterMainNumber {
void OnChangeFilterMainNumberListener(int totalCheckedNumber);
}
ChangeFilterMainNumber listener;
FilterMainListAdapter(ChangeFilterMainNumber listener) {
this.listener = listener;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) {
listener.OnChangeFilterMainNumberListener(position)
}
}
});
}
}
class FilterSubListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
}
I'm trying to create a custom view that has a content area and a progress bar so that any inheriting custom view would have it's layout inflated in the content area. That way you could show/hide the progress bar on the child view.
#EViewGroup(R.layout.loading_view)
class LoadingView extends RelativeLayout{
// some code
#ViewById FrameLayout content;
#ViewById ProgressBar pb;
public View getFrameLayout(){
// sub-classes will use this to inject their content
return content;
}
void setIsLoading(boolean l){
// shows\hides pb
}
}
#EViewGroup(R.layout.other_view)
class OtherView extends LoadingView{
#ViewById TextView someTxt;
public OtherView(...){
// inject to getFrameLayout()
}
}
Now this doesn't remotely work. Here is why:
#EViewGroup on OtherView overrides the one on LoadingView so R.layout.loading_view doesn't even gets loaded. it only gets loaded if you specify #EViewGroup(R.layout.loading_view)
if you do specify the same layout , there is no point in having Android Annotations in the first place since it will not inject view in OtherView.
Is there any way at all to make this work ? Any ideas ?
If I have understood you correctly, you could solve overriding the method:
#EViewGroup(R.layout.loading_view)
class LoadingView extends RelativeLayout {
// some code
#ViewById FrameLayout content;
#ViewById ProgressBar pb;
public View getFrameLayout(){
return content;
}
void setIsLoading(boolean l){
// shows\hides pb
}
}
And each child implement the method returning its content:
class OtherView extends LoadingView {
#ViewById TextView someTxt;
#Override
public View getFrameLayout() {
View content = inflate(R.layout.other_view);
return content;
}
}
As you said, If you put #EViewGroup(R.layout.other_view) in child class, AndroidAnnotations overwrite the main layout view.
I am using Android Annotations 3.1 in my project in Android Studio and I am using an Expandable List View.
I am adding a header to my list like this:
View header = getLayoutInflater().inflate(R.layout.list_view_header, null);
txt_user = (TextView) header.findViewById(R.id.txt_user);
txt_user.setText(" ... ");
mExpandableListView.addHeaderView(header);
Question:
How can I use #ViewById to set my TextView to:
- avoid using findViewById
- changing its text
- and then adding that as a header during:
#AfterViews
protected void init() {
....
}
If I do this inside my Activity when using my text view inside init:
#ViewById
TextView txt_user;
It gives null pointer exception.
Why don't you create a custom view like this :
#EViewGroup(R.layout.list_view_header)
public class MyHeader extends LinearLayout {
#ViewById(R.id.txt_user)
protected TextView mTextView;
public void setText(String text) {
mTextView.setText(text);
}
public MyHeader(Context context) {
super(context);
}
}
Then, in your Activity, write this :
#EActivity(R.layout.activity_xxx)
public class MyActivity extends Activity {
private MyHeader mHeader;
#AfterViews
protected void init() {
mHeader = MyHeader_.build(this);
mHeader.setText("...");
mExpandableListView.addHeaderView(mHeader);
}
}
To use a view signed with #ViewGroup within a layout, you need to implement the constructors that take AttributSet in its class.
NOTE: Remember to use the generated class that has a _ (underline) at the end.
She who must be declared in the layout xml.