Android Annotations: custom view inheritance? - android

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.

Related

Accessing navigation header elements using ButterKnife

I have a class which handles character selection from a RecyclerView and everything works, but I want to update text of the elements in the NavigationView header with the right information. So far I've been trying to use ButterKnife to solve this, but with no success. However, I've been able to make it work in this way:
private ImageView mImageView;
private TextViewTitle mTextViewName;
private TextViewRegular mTextViewTitle;
private static View mHeaderView;
public void setHeaderView(View headerView) {
mHeaderView = headerView;
selectedCharacterInstance.setHeaderViewElements();
}
private void setHeaderViewElements() {
mImageView = mHeaderView.findViewById(R.id.selected_character_info1);
mTextViewName = mHeaderView.findViewById(R.id.selected_character_info2);
mTextViewTitle = mHeaderView.findViewById(R.id.selected_character_info3);
}
I pass the headerView from the MainActivity. I don't like this approach, but I might be wrong since I am fairly new to Android programming. Is this the right approach? Is there a way to solve this using ButterKnife? (I tried ButterKnife but the ImageView and the TextViews were always null)
I use also Butter Knife for my navigation header. For the header, I create a view holder:
protected static class HeaderViewHolder {
#BindView(R.id.user_name)
protected TextView mUserNameTxt;
#BindView(R.id.user_email)
protected TextView mUserEmailTxt;
HeaderViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
Then in my activity's onCreate method:
View header = mNavigationView.getHeaderView(0);
mHeaderViewHolder = new HeaderViewHolder(header);
mHeaderViewHolder.mUserEmailTxt.setText(userEmail);
This allows me to use mHeaderViewHolder like any other RecyclerView holder.

Extending ViewGroup with #EViewGroup annotation

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).

How to use #ViewById for an inflated view outside of #EActivity(R.layout.activity_x)

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.

Control a View in a Fragment after Asynctask

I have a Fragment that loads data from web
So I'd like to make a RelativeLayout to show to user that data is still loading
When finished I want to make my RelativeLayout disappear
But here's the problem..
On my Xml of Fragment I put my RelativeLayout with my loading bar visible..
So my fragments goes throught this step:
1)onCreate() {inside this I have Asynctask.execute()
2)onCreateView() {And here I can manage my RelativeLayout with loadingbar through Inflater and View}
3)Asyntask.onPostExecute() {And here I want to make disappear my relativelayout..}
BUT in Asynctask there's no way to access to my relativelayout, and of course if I try app crashes because of NullPointerException [Obvious]
How can I manage this problem?
It seems he did the following:
private RelativeLayout mRelativeLayout;
mRelativeLayout = findViewById(R.Layout.relativeLayoutId);
And use it later on in the class like
mRelativeLayout.doTheMagic();
Modify the constructor of your AsyncTask to accept a reference to the View, then you can modify it during the onPreExecute() and onPostExecute(Result) methods. Why aren't you just using a ProgressDialog instead? Much easier.
That's how I solved:
public class HomeFragment extends Fragment {
RelativeLayout mLoading_Bar;
public void onCreate(Bundle savedInstanceState) {
Asynctask.execute();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View cView = inflater.inflate(R.layout.fragment_home, container, false);
mLoading_Bar = (RelativeLayout) cView.findViewById(R.id.fragment_home_loadingdata_layout);
return cView;
}
class ASynctask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... args) {
//do something
}
protected void onPostExecute(Void string) {
mLoading_Bar.setVisibility(View.GONE);
}
}
}
And that's all folks

Replace a view with other view By code

I want to replace a view with other other by code
For example i need to change a imageview to progressBar by code.
public void changeToProgressBar(ImageView imageview,Context context){
ProgressBar one = new ProgressBar(context);
one.setLayoutParams(imageview.getLayoutParams());
imageview.setVisibility(View.GONE);
one.setVisibility(View.VISIBLE);
//NOW I NEED TO PLACE 'one' EXACTLY THE PLACE WHERE 'imageview' WAS THERE
}
I need to use this many place. I know to set by sending the parent viewgroup.
Is there anyway to get the parent viewgroup from imageview
Thanks
This is trivial to do and other posters have linked to or hinted at the solution. You can get the parent of a View by View.getParent(). You need to cast the returned value to a ViewGroup (q: can a View's parent be anything else than a ViewGroup?). Once you have the parent as a ViewGroup you can do ViewGroup.removeChild(viewToRemove) to remove the old view and add the new one using ViewGroup.addChild(viewToAdd).
You might also want to add the new view at the same index as the remove view to make sure that you don't put the new view on top of or below other views. Here's a complete utility class for you to use:
import android.view.View;
import android.view.ViewGroup;
public class ViewGroupUtils {
public static ViewGroup getParent(View view) {
return (ViewGroup)view.getParent();
}
public static void removeView(View view) {
ViewGroup parent = getParent(view);
if(parent != null) {
parent.removeView(view);
}
}
public static void replaceView(View currentView, View newView) {
ViewGroup parent = getParent(currentView);
if(parent == null) {
return;
}
final int index = parent.indexOfChild(currentView);
removeView(currentView);
removeView(newView);
parent.addView(newView, index);
}
}
Something to consider is that you'll lose any positioning in a relative layout when you replace one view with another. One solution to this would be to make sure that the view you want to replace is wrapped in a another view and that wrapped container view is the one that is positioned in a relative layout.
Retrieve the view you would like to change by calling findViewById() from the activity level http://developer.android.com/reference/android/app/Activity.html#findViewById(int) Then find the sub view you would like to change http://developer.android.com/reference/android/view/View.html#findViewById(int)
Then use the functions from http://developer.android.com/reference/android/view/ViewGroup.html to manipulate the view.
Just as User333 described that could be one solution..
It's also possible to delete the imageview by calling yourview.removeView(imageview) and then create your progress bar and put that inside the view instead by yourview.addView(progressbar)
You can change Android Activities view from any other simple java class or in other activity.
you only need to pass current view and get your element by this view you want to change As :
public class MainActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Setting UI
View currentView = getWindow().getDecorView().getRootView();
//showHeaderLayout is simple java class or it can be any activity
changeLayout.setView(currentView);
}
Class : changeLayout
public class changeLayout{
public static View setView(final Activity activity, final View myView)
{
// myView helps to get Activity view , which we want to change.
TextView tv = (TextView) myView.findViewById(R.id.tv);
tv.setText("changs Text Via other java class !!");
}
}
Note : Passing view makes you able to change any activity view outside an Activity.
I think following ould be the best approach as in this we don't need to set layout params.
setvisibility(Visibility.GONE)
setvisibility(Visibility.VISIBLE)
I am sure that following link not only help you, but even shows you the direction for your requirement. https://stackoverflow.com/a/3760027/3133932
For this, take both imageview and progressBar and set the visibility according to your requirements.
For example
If you want progressBar to be visible, put setvisibility(Visibility.GONE) for imageview and put setvisibility(Visibility.VISIBLE) for progressBar

Categories

Resources