Android Annotations Custom View - java

I am trying to implement Android Annotations to my View. But I cant figure out how to do it correctly. My Problem at the moment is that the fields in the View are always NULL.
I think I have a bit of an understanding problem how to use Android Annotations with Views and Adapters. Can someone give me a hint how I would do this correctly?
In my fragment I use the following adapter:
ItemAdapter
#EBean
public class ItemAdapter extends BaseAdapter {
public ItemAdapter(Context context) {
this.context = context;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
GalleryListView view;
if (convertView == null) {
view = GalleryListView_.build(context);
} else {
view = (GalleryListView) convertView;
}
imageUrls = getDocumentListAll();
DDocuments doc = documentProxy.getElementByDocument(imageUrls.get(position));
view.init(imageUrls.get(position), Uri.fromFile(new File(doc.getPath())));
// before I used annotations I did set my Image using this. But now I dont really know how to use this line
// ImageLoader.getInstance().displayImage(Uri.fromFile(new File(doc.getPath())).toString(), holder.image, options, animateFirstListener);
}
}
GalleryListView
#EViewGroup(R.layout.gallery_list)
public class GalleryListView extends LinearLayout {
#ViewById
ImageView image;
#ViewById
TextView text;
public void init(String imageText, Uri imageUri) {
text.setText(imageText);
image.setImageURI(imageUri);
}
}

The problem is that the Views have not been injected when you're making your call to init. So, a solution should be to not set the view's text directly; rather, you want to set a variable that you know will be read into the Views after they have been injected.
One way to do this is to make use of #AfterViews. A method with the #AfterViews annotation will be called after View injection has taken place.
Off the top of my head it would look like this:
#EViewGroup(R.layout.gallery_list)
public class GalleryListView extends LinearLayout {
String mText;
Uri mUri;
#ViewById
ImageView image;
#ViewById
TextView text;
public void init(String imageText, Uri imageUri) {
mText = imageText;
mUri = imageUri;
}
#AfterViews
void afterViews() {
text.setText(mText);
image.setImageURI(mUri);
}
}
This might not be the best solution, but it should get the job done. I suggest keeping the Android Annotation Cookbook handy; there are other similar annotations that will come in handy.

Where are you getting context from?
Try replacing
view = GalleryListView_.build(context);
with
view = new GalleryListView_(context);
In any case, for such a small view I don't see the profit of using Android Annotations, maybe if you had multiple resources I would understand, but for such little code, I recommend you to implement te constructor and inflate the resources yourself there.

Related

Custom ListView in a fragment, what files do I need?

I already created this with custom adapter in another project, but I didn't use fragments. I now have a project using fragments, and am displaying the listview in a fragment. I don't know or am able to find exactly what rules and what classes/java files I need for this to work in a fragment.
Every example on the internet I've used develops an error in some way, and since I don't understand every aspect of how this is done I can't fix it on my own.
In my previous project, I did this (CalculationsActivity.java):
public class CalculationsActivity extends AppCompatActivity implements Serializable {
//content of my class
}
class CustomAdapter extends BaseAdapter {
#Override
public int getCount() {
return arrayLi.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
view = getLayoutInflater().inflate(R.layout.customlayout, null);
ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
TextView content = (TextView) view.findViewById(R.id.content);
TextView date = (TextView) view.findViewById(R.id.date);
imageView.setImageResource(R.drawable.calcer);
String[] convertedArrLi = arrayLi.toArray(new String[arrayLi.size()]);
String[] convertedDates = dates.toArray(new String[dates.size()]);
content.setText(convertedArrLi[i]);
date.setText(convertedDates[i]);
return view;
}
}
And from that I set an adapter to my listview. This class was in the same java file as the activity that the listview was displayed in. If I do this in my fragment, I get a bunch of red lines. getLayoutInflater() and variables from my other class above it will be red.
As I understand so far you need MainActivity.java, MyFragment.java and Adapter.java. To make your listview work with your array you need to set an adapter including the current activity and the array you want to use. If someone can explain what files I actually need and how they work together (send info to each other and start one another), I would appreciate it.
Note: I have to use a custom adapter.
You can use your custom adapter in activity and in fragment, there is no difference. Can you provide screenshot of your bunch of red lines?
Also your question is incorrect: "what files do I need?". You need classes, first class for activity or fragment to create/declare/initialize second class (custom adapter) and fill it with data.

Using a Fragment as the Context in Android

I'm trying to follow this tutorial
I have a project that uses the Sidebar Navigation, so I have one MainActivity and multiple Fragments. At ~6:20 into the video, you can see the following code:
PersonListAdapter adapter = new PersonListAdapter(
this,
R.layout.adapter_view_layout,
peopleList);
The constructor for the PersonListAdapter Class is:
public PersonListAdapter(Context context, int resource, ArrayList<Attacks> objects) {
super(context, resource, objects);
this.mContext = mContext;
mResource = resource;
}
The problem lies with Context.
If I use the word "this", there is a red line.
If I replace
"this" with "getActivity()", there is no red line, but the app
crashes when I run it.
I've also tried "this.getContext()" and "this.getActivity()"
I have also tried replacing "this" with "getActivity().getApplicationContext()", and the app crashes.
The tutorial uses MainActivity.java, but my code is in FragmentCharacters.java. I don't know what I'm supposed to write in place of "this", or if I need to change something in the PersonListAdapter class for Context.
You cannot use a Fragment as a Context, because Fragment doesn't inherit from Context.
However, if you consult the Fragment lifecycle, you can see that the Fragment has access to its host Activity at any time between the lifecycle callbacks OnActivityCreated() and onDestroyView(). If you try to access the Activity before OnActivityCreated(), for example, it will probably return null.
So make sure you are calling getActivity() from within onActivityCreated() or later, which will make sure your Activity is available.
UPDATE, I Provided a Case Example inside the Code Snippets as well, and I chose "FragmentName" as Fragment name for example.
First Look at This Fragment Structure.
I Added [mAdapter] in Both onCreate and onCreateView
And I Added FragmentName.this for the Forth argument
The Reason is, You can send data from The Adapter to Other Activities with it, for Example FragmentName.mAdapter.getLayoutPosition()
But, Let's assume We have an ImageView which is In MainActivity and we want to use it in our Adapter, So let's Establish an ImageView In our Fragment as well, Notice I Declared the ImageView Inside onCreate
And, For Another Example, Let's Assume we Have a Public Void at the End of our Fragment as Well, It can be Literally Anything. I Named it ExampleClass
/////////FIRST TAKE A LOOK AT FRAGMENT//////////
public class FragmentName extends Fragment {
PersonListAdapter adapter;
ImageView imageView; // For Example thi ImageView is from MainActivity
public FragmentName() {
...
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
adapter = new PersonListAdapter(getContext(), R.layout.adapter_view_layout, peopleList, FragmentName.this);
imageView = (ImageView) getActivity().findViewById(R.id.ImageView);
// This Imageview is in Another Activity, Like MainActivity
// So we Need to Find it Using 'getActivity()'
...
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
adapter = new PersonListAdapter(getContext(), R.layout.adapter_view_layout, peopleList, FragmentName.this);
}
}
public void ExampleClass(int color, ...) {
...
}
////////////////////////////////////////////////////////////////
Now, Let's take this Example into our Adapter as well, to Show how it can be Used.
But, In the Adapter Use [FragmentName], Instead of [Fragment] like Below:
///////////NOW INSIDE YOUR ADAPTER/////////////
public class PersonListAdapter extends RecyclerView.Adapter < PersonListAdapter.myViewHolder > {
FragmentName myFragment; // SEE WHAT HAPPENDED HERE?
...
public PersonListAdapter(Context context, int resource, ArrayList < Attacks > objects, FragmentName fragment) {
super(context, resource, objects);
this.mContext = mContext;
mResource = resource;
this.myFragment = fragment
}
#Override
public PersonListAdapter.myViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Example use of myFragment
// Lets Execute ExmpleClass inside the Fragment
myFragment.ExampleClass(int color, ...);
// Let's Use the ImageView from MainActivity Here
myFragment.imageView.setImageRresource(...);
...
}
...
// YOU CAN NOW USE "myFragment" As a Context In your Adapter
The Good Part about this is That You can Use Fragment As CONTEXT in Your PersonListAdapter
Update: The second Code, onCreateViewHolder is wrong, it has to be inside a ClickListener in ViewHolder or onBindViewHolder

Android: How to scroll a parentView when child containing recyclerView is scrolled

I have created an app that contains a viewPager inside the mainActivity, this viewPager contains 5 fragments, of which 4 are recyclerViews and one is a normal linearLayout containing some text...
Here is a screenshot of the app, not all tabs in the tablayout are visible:
Now, as you might have seen already, there isn't much space in the viewPager for the user to see anything, so they have to scroll too much to view things in the recylerView. I want to modify my app so that when the user tries to scroll inside the recyclerView, the visible part of the mainActivity is scrolled down till the recyclerView occupies the entire page and then the recyclerView begins to scroll normally.
Can someone please help me implement this type of scroll feature into my app. You can just check out this app for a reference to what I'm saying. Just open up any movie or tvSeries and then try scrolling, the mainActivity gets scrolled first and then the rest of the layout.... Can someone please help. I've already tried the solutions on stackOverflow and many of them don't work, I also tried to google for a solution, but didn't get anything useful....
Here is the code for my adapter:
public class cardViewAdapterCreditsCast extends RecyclerView.Adapter<cardViewAdapterCreditsCast.ViewHolder> {
private Context context;
private List<creditsModel> creditsModels;
public cardViewAdapterCreditsCast(Context context, List<creditsModel> creditsModels) {
this.context = context;
this.creditsModels = creditsModels;
}
#Override
public cardViewAdapterCreditsCast.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_cast_row, parent, false);
return new cardViewAdapterCreditsCast.ViewHolder(view);
}
#Override
public void onBindViewHolder(cardViewAdapterCreditsCast.ViewHolder holder, int position) {
final creditsModel creditsModel = creditsModels.get(position);
int tempNumber = holder.celebImage.getWidth();
holder.celebImage.setMinimumHeight(tempNumber);
holder.celebImage.setMaxHeight(tempNumber + 1);
String imagePath = "https://image.tmdb.org/t/p/w500" + creditsModel.getProfilePath();
holder.starringAs.setText(creditsModel.getCharacter());
holder.celebName.setText(creditsModel.getActorName());
if (creditsModel.getProfilePath() != null) {
Picasso.with(context).load(imagePath).transform(new CircleTransform()).into(holder.celebImage);
} else
holder.celebImage.setBackgroundResource(R.drawable.not_found);
}
#Override
public int getItemCount() {
return creditsModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView celebImage;
public TextView celebName;
public TextView starringAs;
private LinearLayout linearLayout;
public ViewHolder(View itemView) {
super(itemView);
linearLayout = (LinearLayout) itemView.findViewById(R.id.castRowMainLinearLayout);
celebImage = (ImageView) itemView.findViewById(R.id.castRowImage);
celebName = (TextView) itemView.findViewById(R.id.castRowName);
starringAs = (TextView) itemView.findViewById(R.id.castRowAppearance);
}
}
}
Use nested Scrollview if there are more than one scrollview or recyclerview.
You can use a CoordinatorLayout to implement this easily.
Example, http://saulmm.github.io/mastering-coordinator
Documentation, https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.html

Use same fragment in ViewPager but fragment will have different layout each time

I want to keep my application thin.
Problem: I would like to reuse my Fragment class code to create 3 different instances in the ViewPager which will have 3 pages. Each Fragment will have a different ImageView or background Drawable. What are best practices regarding this? I noticed that using factory methods like here seem to be good, any other alternatives?
I have one Fragment which has the following methods:
Fragment.java
public static Fragment newInstance(Context context) {
FragmentTutorial f = new FragmentTutorial();
Bundle args = new Bundle();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment, null);
return root;
}
I have a ViewPagerAdapter class which has the following methods:
ViewPagerAdapter.java
public ViewPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
#Override
public Fragment getItem(int position) {
return new FragmentTutorial().newInstance(mContext);
}
#Override
public int getCount() {
return totalPage;
}
What I've found is the "best" way to do it (in my opinion, of course) is to do the following:
Have the fragment contain methods to set the customizable data (background, text, etc)
Note: Be careful of trying to load the data in when first creating the fragment. You may be able to set the data before onCreateView() even runs, or at other times it may run after onCreateView(). I personally use a boolean to check if the data has been set. Inside onCreateView() [or onActivityCreated()], I check if the data has been set already. If it has, load in the data. Alternatively, while setting the data, I check if the views have been created/cached already. This is done by simply having variables to cache the data, say private ImageView mBackgroundView. If the view is not null, then I safely set the data on the views.
The above is also an alternative to using newInstance, although both methods work pretty well. However, for more flexibility, I only use newInstance if a) the data is already known before the fragment has to be inserted and b) the data doesn't need to change according to input from elsewhere much.
Let the ViewPager handle all the data
Pass in all the data - a list of ImageViews, a array of Strings, define where all the data is in Resources, etc - at the very beginning [say, in the constructor]
Have the ViewPager create an ArrayList of the fragments- set up each fragment as early as possible (say when first getting all the data) and add it to the list
Let getCount() just use the size of the list
Let getItem() just get the item in the list at the position
Note: If you have any dynamic data, set it up in the getItem() method. Furthermore, you can always add more data+fragments during runtime as well [just notify the adapter that the dataset has been changed]
Essentially, the fragment is like a simple servant- it does simply the least work necessary. If it doesn't have to handle choosing the data, all the better. It'll thus be far more flexible. Just give methods to set the data/views appropriately on the fragment. Now, the ArrayAdapter can do all the grimy hard work with managing the data and giving it to the appropriate fragment. Take advantage of that.
Now, note that this is assuming you want to use a single layout but want to change different aspects of that layout (texts, background, etc). If you want to make a master fragment class that can use any sort of defined layout, you can but note that it decreases the runtime flexibility (how can you change the text or background to something you get from the internet? You simply can't if you only can define and choose from pre-set layouts).
Either way, the ArrayAdapter should take care of all the different data while the fragment simply does as it's designed to do, in a more flexible manner preferably.
Edit:
Here is the project where I most recently implemented this sort of pattern. Note that it has far more to it, so I'll replace it with some not-so-pseudo pseudo-code in the morning/afternoon.
ViewPager [a bit sloppy with all the different things I was trying to do, including extending from a FragmentStatePagerAdapter without actually using any of the specific features of a StatePagerAdapter. In other words, I still need to work on the lifecycle implementations everywhere]
Fragment [Also may be a bit sloppy but shows the pattern still]
The object (actually another fragment) that uses the ViewPager [it's actually a "VerticalViewpager" from a library, but other than the animations and direction to change the current fragment, it's exactly the same- particularly code-wise]
Edit2:
Here is a more (if overly) simplified example of the pattern described above.
Disclaimer: The following code has absolutely no lifecycle management implementations and is older code that has been untouched since around August '14
Fragment simply allows the user of the fragment to set the background color and the text of the single TextView
Link to BaseFragment
Link to layout file
The adapter creates three instances of the fragment and sets the background color and text of each. Each fragment's text, color, and total fragments is hard coded.
Link to Activity+adapter
Link to layout file
Now, here are the exact relevant portions of the code:
BaseFragment
// Note: Found out later can extend normal Fragments but must use v13 adapter
public class BaseFragment extends android.support.v4.app.Fragment {
FrameLayout mMainLayout; // The parent layout
int mNewColor = 0; // The new bg color, set from activity
String mNewText = ""; // The new text, set from activity
TextView mMainText; // The only textview in this fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the fragment's layout
View view = inflater.inflate(R.layout.fragment_base,container,false);
// Save the textview for further editing
mMainText = (TextView) view.findViewById(R.id.textView);
// Save the framelayout to change background color later
mMainLayout = (FrameLayout) view.findViewById(R.id.mainLayout);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// If there is new text or color assigned, set em
if(mNewText != ""){
mMainText.setText(mNewText);
}
if(mNewColor != 0){
mMainLayout.setBackgroundColor(mNewColor);
}
}
#Override
public void onStart(){
super.onStart();
}
// Simply indicate to change the text of the fragment
public void changeText(String newText){
mNewText=newText;
}
// Simply indicate to change the background color of the fragment
public void changeBG(int color) {
// If no color was passed, then set background to white
if(color == 0)
{
mNewColor=getResources().getColor(R.color.white);
}
// else set the color to what was passed in
else{
mNewColor=color;
}
}
}
MyAdapter
class MyAdapter extends FragmentPagerAdapter{
// Three simple fragments
BaseFragment fragA;
BaseFragment fragB;
BaseFragment fragC;
public MyAdapter(FragmentManager fm) {
super(fm);
}
public void setFragments(Context c){
// Set up the simple base fragments
fragA = new BaseFragment();
fragB = new BaseFragment();
fragC = new BaseFragment();
Resources res = c.getResources();
fragA.changeText("This is Fragment A!");
fragB.changeText("This is Fragment B!");
fragC.changeText("This is Fragment C!");
fragA.changeBG(res.getColor(R.color.dev_blue));
fragB.changeBG(res.getColor(R.color.dev_green));
fragC.changeBG(res.getColor(R.color.dev_orange));
}
#Override
public Fragment getItem(int position) {
// TODO: Make this more efficient, use a list or such, also comment more
Fragment frag = null;
if(position == 0){
frag = fragA;
}
else if(position == 1){
frag = fragB;
}
else if(position == 2){
frag = fragC;
}
return frag;
}
#Override
public int getCount() {
return 3;
}
}
You need to pass some sort of id along with newInstance() while creating instance. And according to that id you can use if..else to choose layout file.
See my reference code below:
int id;
public static Fragment newInstance(Context context, int id) {
FragmentTutorial f = new FragmentTutorial();
Bundle args = new Bundle();
this.id = id;
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(id == 1)
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment1, null);
else
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment2, null);
return root;
}
Can't you just introduce fields to the Fragment class to account for the variances in background, etc. and add them to its constructor? Then in getItem instantiate the Fragment class with different values depending on the value of position.

General Question about Android Gallery Control

So, I've been doing xml layout for a project that involves getting a horizontal scrollable row of images on a screen, and did so using just a horizontalscrollview. and a bunch of imagebuttons. I used an include to put this on another xml layout page and another programmer will then populate the images dynamically.
My question is, how would the gallery control benefit us? I haven't done much Java programming and I've seen some instruction online of how to implement this control, but not a lot on WHY you would use this. It looks like this control works mainly via Java insertion via array, but other than that I can't tell what the benefits are from reading over my way of just creating the layout and having this other programmer insert his own images manually.
Another related question - do these images for a gallery need to me imageviews, or can they be imagemaps? Currently they are imagemaps because we want them to be clicable to go to a user's profile, etc.
Thanks!
Gallery is nearly perfect. In one of my projects I do have a LinearLayout with a Gallery in it:
<Gallery
android:id="#+id/gallery"
android:layout_height="0dip"
android:layout_weight="1"
android:layout_width="fill_parent"
android:spacing="2dip" />
An activity implements OnItemClickListener:
public class MyActivity extends Activity implements OnItemClickListener {
A data structure contains all items and is send to an adapter:
private void processGallery() {
adapter = new MyAdapter(this, containers, appName);
if (adapter != null) {
gallery.setAdapter(adapter);
}
}
#Override
public void onItemClick(final AdapterView<?> adapterView, final View view, final int position, final long id) {
if (containers != null) {
container = containers.get(position);
if (container != null) {
// Handle selected image
}
}
}
The adapter is a usual BaseAdapter - nothing magic:
public class MyAdapter extends BaseAdapter {
private ArrayList<Container> containers;
private Context context;
public int getCount() {
return containers.size();
}
public Object getItem(final int position) {
return containers.get(position);
}
public long getItemId(final int position) {
return position;
}
public View getView(final int position, final View contentView, final ViewGroup viewGroup) {
ImageView imageView = new ImageView(context);
Container container = containers.get(position);
if (container != null) {
// Do your image thing here
}
return imageView;
}
public MyAdapter(final Context context, final ArrayList<Container> containers, final String appName) {
this.context = context;
this.containers = containers;
}
}
This simple code gives a horizontal scrolling image gallery with clickable items. The click is send to the activity - no need to do something fancy in the adapter. I removed from the code shown here a DrawableCache that I use because my items do come from the web.

Categories