Creating a professional in app user interface in Android - android

Current situation:
We have different userinterfaces, which are build as fragments, for example the MenuFragment:
public class MenuFragment extends Fragment implements Hideable, View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.view = inflater.inflate(R.layout.menu, container, false);
}
#Override
public void hide() {
view.setVisibility(View.GONE);
}
#Override
public void show() {
view.setVisibility(View.VISIBLE);
}
}
Hideable:
public static interface Hideable {
void hide();
void show();
}
States are enums:
public enum InterfaceState {
STANDARD, TWO, THREE ;
private List<Hideable> visibleElements;
private void setVisibleElements(Hideable[] visibleElements) {
this.visibleElements = Arrays.asList(visibleElements);
}
}
Set them using:
InterfaceState.STANDARD.setVisibleElements(new Hideable[] { menuFragment });
where menuFragment is
menuFragment = (MenuFragment) activity.getFragmentManager().findFragmentById(R.id.menu_fragment);
Now i change the state calling changeToState:
public void changeToState(InterfaceState state) {
List<Hideable> hideList = new LinkedList<>();
for (Hideable e : this.currentState.visibleElements) {
if (!state.visibleElements.contains(e))
hideList.add(e);
e.hide();
}
List<Hideable> showList = new LinkedList<>();
for (Hideable e : state.visibleElements) {
if (!this.currentState.visibleElements.contains(e))
showList.add(e);
e.show();
}
The system seems to be buggy. Sometimes UI elements dont appear.
Questions:
Is this a good way to implement an UI? If not, what's a better way to do?
Does Android allocate space for a view, which has Visibility="GONE"? To use the upper mechanism, I need to define all UI elements (like MenuFragment menuFragment) directly on app start.
Any suggestions/improvements?
Thanks to all readers.

I always advocate the convention over configuration rule. Android has some nice conventions on how to design you user interface and you should s tick to them whenever possible.
No it does not. That is, View.GONE makes the view act as though it's not there (from the layouts point of view), where as View.INVISIBLE allocates the layout space needed for the view, but simply makes the view invisible. However, even with View.GONE, you'll still be able to get your view with findViewById().
I think that storing information in an enum like that is bad idea. You should find a different method that suits you (either put your list in an activity, in the Application class or something similar.)
EDIT: You can find some usefull information about the pattern on Wikipedia, and you can read more about the Android Design Guidelines here. There is also a nice document about the pattern here. You can also look at AOSP's code style guidelines (or, well, rules) as they provide some nice conventions on how to write code.

Related

Is it possible to disable access to fragment's layout(views) from parent activity?

I'm working on a library which will provide fragment with some input fields in it. These input fields will contain user's private information that app which uses my library should not have access to. Therefore edittexts or we can say fragment's layout cannot be accessed from activity(findViewById,getChildAt..) where this fragment is attached to.
Usage of dialog, or another activity is not acceptable, this fragment should be included directly in activity's layout.
Is this even possible in Android ?
I was thinking of creating views dynamically, and overriding methods such as getChildAt to prevent access to child views, but before I start "playing" with this problem, I'd rather ask here for some opinions.
Android does not provide a model for such a usage.
Overriding methods will certainly make it harder to access these views, but not impossible. Your custom view class has to store its children somewhere. Even if that is a private field, reflection can access it.
An activity has full control over his content, and I don't think you can prevent that.
First of all what you want is not a good approach, and what i am suggesting is just an idea, its not tested and recommended but can do your work
Create class BaseFragment and extend you each class with Base Fragment
must override its getView()
In these approached you have to remove root view as a class member getView returns the same
public class BaseFragment extends Fragment {
#Nullable
#Override
public View getView() {
super.getView();
}
}
Now you can do it in two ways
Create boolean in BaseFragment with private access boolean canAccess = true; with no getter and setter and change definition of your getView() to
public BaseFragment() {
canAccess = false;
}
#Nullable
#Override
public View getView() {
if(canAccess)
return super.getView();
return
null;
}
You must call super() for your every child constructors, now if you access getView inside the class canAccess is true so you will get actual view otherwise you will get null.
As per documentation
Get the root view for the fragment's layout (the one returned by {#link #onCreateView}),
if provided #return The fragment's root view, or null if it has no layout.
Second option is much simplest
#Nullable
#Override
public View getView() {
try {
throw new Exception("Who called me?");
} catch (Exception e) {
String className = e.getStackTrace()[1].getClass().getCanonicalName();
if (className.equals(YourParentActivity.class.getCanonicalName()))
return null;
else
return super.getView();
}
}
You can disable contents inside your fragment using following method:
public void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
int childCount = viewGroup.getChildCount();
for(int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
}
}
}
You can simply call above method as follows:
enableDisableViewGroup((ViewGroup) rootView, true); // disable
enableDisableViewGroup((ViewGroup) rootView, false); // enable
This method will work for both fragments and adapters to disable/enable their contents.
I did not understand it correctly I guess, but I think that anything created by private access mode cannot be accessed from outside.
Did you ever consider using webview for your particular problem !!!
make a fragment and in it show your desired webview and let user input anything he likes.
by this the OTHER APP wont have access to the EditTexts.
You can override TextView's getText() and return null for private views. If someone will get this text view - he will not be able to get it's content.

Databinding apply for one layout used by multiple activity/fragment

I am replacing existing code by databinding. But I face a problem.
I have some layout files shared by more than one activity/fragment. E.g there is a layout file layout_sub used by SubFragmentA and its extending class SubFragmentB. And the data model used in these two fragment are not the same.
The code looks like following.
public class SubFragmentA extends Fragment {
private DataA dataA;
#Override
public View onCreateView(Bundle Bundle) {
View v = LayoutInflator.from(getActivity()).inflate(R.layout.shared_layout);
initView(v, dataA);
return v;
}
private void initView(view v, DataA dataA) {
// use dataA to init v
}
}
public class SubFragmentB extends Fragment {
private DataB dataB;
#Override
public View onCreateView(Bundle Bundle) {
View v = LayoutInflator.from(getActivity()).inflate(R.layout.shared_layout);
initView(v, dataB);
return v;
}
private void initView(view v, DataB dataB) {
// use dataB to init v
}
}
So far, I think using DataA and DataB in layout_sub file at the same time is not a good idea, because it would require a lot of redundant code to decide which object to be used.
Please share your ideas on this problem.
Finally, I got a solution. The databinding is used for MVVM pattern. That means one layout corresponds to one ViewModel. And the ViewModel contains every data for UI layout. So I should prepare one ViewModel for each layout file. And every fragment/activity should just handle the ViewModel.

Challenges in RecyclerView of Android L

I have been playing around with RecyclerView for a little bit. Is there any easy way to put OnClickListener for items in RecyclerView? I have tried implementing it in ViewHolder. The onClick event never got triggered.
And I have used notifyItemInserted(position) for adding new value into RecyclerView. The UI does not got refreshed automatically. Needed to pull up and down to refresh. But when I invoke notifyDatasetChanged(..), it is ok.
I have applied DefaultItemAnimator to RecyclerView. But, not seeing any animation when new item added.
Thanks advance for any idea.
This is the first Android L component I have tested out and I am stucking there.
Here is my Adapter class:
public class AdapterRecyclerView extends RecyclerView.Adapter<AdapterRecyclerView.MyViewHolder> {
private List<String> arrExperiences;
//Provide a reference to the type of views that you are using - Custom ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvExperienceTitle;
public TextView tvExperienceDesc;
public MyViewHolder(RelativeLayout itemView) {
super(itemView);
tvExperienceTitle = (TextView) itemView.findViewById(R.id.tv_experience_title);
tvExperienceDesc = (TextView) itemView.findViewById(R.id.tv_experience_desc);
}
}
//Provide a suitable constructor : depending on the kind of dataset.
public AdapterRecyclerView(List<String> arrExperiences){
this.arrExperiences = arrExperiences;
}
//Create new view : invoke by a Layout Manager
#Override
public AdapterRecyclerView.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RelativeLayout view = (RelativeLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item_recycler, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(AdapterRecyclerView.MyViewHolder viewHolder, int position) {
//get element from your dataset at this position.
//replace the content of the view with this element.
viewHolder.tvExperienceTitle.setText(arrExperiences.get(position));
}
#Override
public int getItemCount() {
return arrExperiences.size();
}
public void addExperience(String experience, int position){
arrExperiences.add(position, experience);
notifyItemInserted(position);
//notifyDataSetChanged();
}
public void removeExperience(){
int index = (int) (Math.random() * arrExperiences.size());
arrExperiences.remove(index);
notifyItemRemoved(index);
//notifyDataSetChanged();
}
}
Simply add this in your Adapter:
#Override
public void onBindViewHolder(AdapterRecyclerView.MyViewHolder viewHolder, int position) {
yourItems.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//do your stuff
}
});
}
Please see my answer here. You do need an extra class (which may be included as part of the full release) but it will allow you to create OnItemClickListeners the way you are used to for ListViews.
Since you still didn't mark correct any answer, and even if it's an old question, I will try to provide the way I do. I think it is very clean and professional. The functionalities are taken from different blogs (I still have to mention them in the page), merged and methods have been improved for speed and scalability, for all activities that use a RecycleView.
https://github.com/davideas/FlexibleAdapter
At lower class there is SelectableAdapter that provides selection functionalities and it's able to maintain the state after the rotation, you just need to call the onSave/onRestore methods from the activity.
Then the class FlexibleAdapter handles the content with the support of the animation (calling notify only for the position. Note: you still need to set your animation to the RecyclerView when you create it the activity).
Then you need to extend over again this class. Here you add and implements methods as you wish for your own ViewHolder and your Domain/Model class (data holder). Note: I have provided an example which does not compile because you need to change the classes with the ones you have in your project.
I think that, it's the ViewHolder that should keep the listeners of the clicks and that it should be done at the creation and not in the Binding method (that is called at each invalidate from notify...() ).
Also note that this adapter handles the basic clicks: single and long clicks, if you need a double tap you need to use the way Jabob Tabak does in its answer.
I still have to improve it, so keep an eye on it. I also want to add some new functionalities like the Undo.
Here you get a simple Adapter class which can perform onItemClick event on each list row for the recyclerview.

Android Fragment and logical application design

This is more of a design question and how one would go about designing applications. I have been having fun with fragments, but one thing that doesn't make sense to me something like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 2);
Button btnOne = (Button) getView().findViewById(R.id.one);
btnOne.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
String currentText = getScreen().getText().toString();
currentText += "1";
getScreen().setText(currentText);
}
});
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.standard_calculator, container, false);
return view;
}
/** Return the screen TextView object for manipulation
* #return screen
*/
public TextView getScreen() {
return screen;
}
Screen title are private variables in the class and this isn't the whole class, but just the part that I need to help my question. There are going to be at least 15 or so buttons that manipulate the screen and it doesn't feel like good practice to and put them all in the onCreate method, I was wondering whether it would be better design to have them in another class that let the methods in the fragment be more specific to the life-cycle of the fragment, although one can say that the buttons are used by the fragment and therefore should be part of the class. Perhaps an "initialising" method is needed.
I was hoping someone might be able to direct me to some design patterns or logical way of thinking about application design, it is quite different.
Many thanks.
Putting them in the XML is less versatile than doing it in code. If you don't want to have XXX anonymous methods, you can make your own Fragment/Class implement View.onClickListener and implement the onClick method:
#Override
public void onClick(final View v) {
if ( v.getId() == R.id.btn_logout ){
// Do One Thing
} else if ( v.getId() == R.id.btn_about) {
// Do Something Else
} else if ( v.getId() == R.id.btn_shutdown) {
// Or Maybe do this :)
}
}
Then in your onViewCreated just assign each button with "this"
final someBtn = (Button) view.findViewById(R.id.btn_logout);
someBtn.setOnClickListener(this);
That can be cleaner looking than a bunch of anonymous methods and you know you have your click listeners in one place.
You don't have to initialize them all in the onCreate() method. In fact, you don't have to initialize them in java code at all. You could simply define them in xml and define an "onClick" property in your xml. The method that you set in "onClick" will be called for that button. It's one way to make your Activities cleaner.

Android animation reduce stutter/choppy/lag

So I've been having animation issues especially when two animations happen at once or right when an activity loads. I understand it's probably a resource problem and a lot of things are going on in the main thread causing the animations to stutter.
I've found a couple interesting suggestions:
1. Threads (ThreadPoolExecutor)
Here: How do I make my animation smoother Android
2. setDrawingCacheEnabled(true)
Here: How does Android's setDrawingCacheEnabled() work?
3. ViewGroup: animationCache = true
Here: http://www.curious-creature.org/2010/12/02/android-graphics-animations-and-tips-tricks/
However I haven't been able to find any sort of examples to implement these things. Any ideas?
I've reduced the amount of stutter on my animations by following these rules listed in order of importance when reducing stutter:
Don't initiate animations in onCreate, onStart or onResume.
Initiate animations on user events such as onClick and disable touch events until animation is complete.
Don't initiate more than 2 animations simultaneously.
If you are using animation you should follow the android docs; in fact in some cases, you might need to postpone your fragment transition for a short period of time. For example in my case I need to postpone my animation until my viewmodel return some data:
Use postponeEnterTransition() in the entering fragment onViewCreated() method:
public class A extends Fragment {
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
...
postponeEnterTransition();
}
}
Once the data are ready to start the transition, call startPostponedEnterTransition()
public class A extends Fragment {
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
postponeEnterTransition();
final ViewGroup parentView = (ViewGroup) view.getParent();
// Wait for the data to load
viewModel.getData()
.observe(getViewLifecycleOwner(), new Observer<List<String>>() {
#Override
public void onChanged(List<String> list) {
// Set the data on the RecyclerView adapter
adapter.setData(list);
// Start the transition once all views have been
// measured and laid out
parentView.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
parentView.getViewTreeObserver()
.removeOnPreDrawListener(this);
startPostponedEnterTransition();
return true;
});
}
});
}
}

Categories

Resources