I'm making a quiz-like game, where user answers each question before they are allowed to go to the next one.
Characteristics of my app:
Each session will have
around 10-30 screens.
In general, the screens are heterogenious in layout
structures, but they can be
classified into 5-6 types.
The expected time that user interacts with each screen is 10-30
seconds
Once user goes to the next screen,
the previous one is not needed
anymore (he never goes back)
I want to have a nice sliding
transition animation when going from
one screen to the next
Implementations I'm considering:
Start a new Activity for each
screen in the 'forwarding' style,
i.e. start the next screen then
finish the current one.
Load all the views before hand and
use ViewAnimator
It looks like none of my current solution is good. Can you help me on a solution that is good in terms of memory consumption, battery consumption, and responsiveness?
Thank you very much.
OK below is what I did. It turned out I manually set the animation
onCreate() {
mAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_in_right);
mViewPool = new View[] { /* A few views for re-using, each of different type */ };
}
proceed() {
nextView = getView(type);
mFrame.removeAllChilds();
mFrame.addView(nextView);
nextView.startAnimation(mAnimation);
}
getView(int type) {
View view = mViewPool[type];
// reset some subviews if neccessary
return view;
}
Where mFrame is whatever ViewGroup you think is appropriate, not neccessarily ViewAnimator. Mine happens to be ScrollView.
If you see any potential problem with this approach, please let me know. Many thanks.
Related
I have an enhanced loop, which will dynamically inflate however many layouts relevant to the number of values held in my array.
This works perfectly however, there is a method being called on each iteration, which also works but there is a big bug that I need help resolving.
Imagine there are 5 items in my array, therefore 5 layouts are inflated, in these layouts there is a little scratchcard type section on the layout.
Now if the user is on page 1, uses the scratchcard, then moves on to page 2, uses the scratchcard etc etc, it works fine.
But if the user is on page 1 and then goes to say, page 5 and then back to page 1 (basically in a random order), the scratchcard doesn't work.
From my understanding, the reason for this is that the method is being called an implemented on each iteration and the view is losing its state if the user scrolls back or scrolls in random orders.
Therefore I need a way to save the created view state in my viewpager.
Is this possible for my scenario? I have tried my best to find a solution, but cannot find something that feels relevant to my question.
Here is a snippet of the code in question. Thanks for any guidance or suggestions!
for (String x : array1) {
//loop out the number of layouts relative to the number of questions held in x
View current_layout = LayoutInflater.from(getActivity()).inflate(R.layout.question_fragment, null);
//use the pageAdapter to add the layout to the users view
pagerAdapter.addView(current_layout);
//call method to add functionality to the scratchcard
isCorrect(current_layout);
}
public void isCorrect(View current_layout) {
ScratchoffController controller1 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view1), current_layout.findViewById(R.id.scratch_view_behind1));
ScratchoffController controller2 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view2), current_layout.findViewById(R.id.scratch_view_behind2));
ScratchoffController controller3 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view3), current_layout.findViewById(R.id.scratch_view_behind3));
ScratchoffController controller4 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view4), current_layout.findViewById(R.id.scratch_view_behind4));
}
I ussually use ViewPager with Fragments and what you mention has happend to me when I try to keep references to the Fragment instances (in my case) outside of the viewpager.
This happens because the viewpager may create new instances of the Fragment it contains when you re-vist the tab in the way you mention. When this happens, the instance reference you hold outside of the viewpager is not anymore what the viewpager is showing.
In your case , according to this question, you have to oveeride instatiateItem and destroyItem. I think you can use these methods to save state restore state, and also you could update any external reference when instantiateItem is called.
I design an app for the restaurant menu, running on Pad. The UI is 2 parts, the left one is a category panel. Clicking one category, the right area will display a fragment.
I feel the fragment switch is too slow. I had thought the reason is at FragmentManager's replace action, but after tracking here, the replace code is just cost around 3 milliseconds. So here is not the display bottleneck. The code is
FragmentManager fragMgr = MainActivity.getInstance().getSupportFragmentManager();
FragmentTransaction trans = fragMgr.beginTransaction();
trans.replace(R.id.dishdisplayarea_layout, frag);
trans.commit();
To improve fragment's display speed, I build the fragments' instance in the MainActivity's onCreate method and store them in a map. Every time while I need to display one, I just get it from the map, so I don't think the time is wasted on building fragment's view.
//get fragment object from map
DishDisplayFragment frag = MainActivity.getInstance().getMapDishDisplayFragments().get(category2Id);
//the fragment's onCreateView function, just return an exist view object
public class DishDisplayFragment extends Fragment {
private View view;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return view;
}
......
}
In every fragment, there is around 30 myself component which I named as DishComponent, and for one DishComponent, including 9 layouts, 6 ImageView, 2 TextView and one button. Since the DishComponent layout XML file is too more code, I just attach a snapshot of it here.
DishComponent structure
I don't know if there too many components in the fragment, then make the fragment replace action very slow. My device is Samsung Tab E with 1.5G memory and a 1.3GHz four-core CPU. I tracked the app's memory, it is less 30M, compared with the device 1.5G memory, the apps consume on memory is not big. So I doubt the bottleneck is CPU. Another evidence is that while I run this app in Android Virtual Device on the computer, the operation is very smooth, obviously, the computer's CPU is more powerful then pad's.
I tracked the CPU time too on Pad. I found most of the time is used for Handler and Choreographer and ViewRootImpl. Actually, I don't know about android's framework. I don't know whether this info is useful to improve my app. I attach it here wish it is useful.
the CPU time for a replace operation of fragment
Then track the CPU graph, I found the CPU only use 30%. Does this mean only one core working for my app and other cores are idle? If so, how can design to make all cores work together?
I also think the point that my pad's hardware is too low level. I used Antutu, an app to measure the phone/pad's running speed. My pad just scores 18000, but the iPhone7 can get 150000 scores on Antutu. So is the hardware the main reason?
Now, I list my questions here
how to locate the position where leads to fragment replace action slow?
how could I use 2 cores when an app needs more calculation?
how to design more optimized layout to make android run/display quickly?
Are there other skills to make fragments replace quickly if the reason is from the hardware?
I'm working on a tile memory game.
A user can press any one of the tiles on a 9x9 board.
However, I want to limit the user to being only to press one at a time.
I've tried researching using a TableLayout, setting beforeAscendants=true, and having that try to manage where a user's click goes, but that proves tedious and saving all those coordinates seems inefficient to the possibility of an easier solution existing.
Right now, I call .setOnClickListener on each of those tiles and setting them to the same onClickListener, but there's no way to limit how many tiles can be pressed at once. I've tried A) setting a synchronized() section around the code inside the anonymous onClickListener code, B) having a boolean to set to true if one tile is already in the onClickListener code but it's simply ignored and C) banging my head against the wall, didn't help
Thanks!
This is the most straightforward and simplest way I can come out with. You should optimize the logic too, it's pretty lengthy here.
onClick(View v) {
switch (v.getId()) {
case R.id.tile1:
// Tile 1 is clicked
// Disable other tiles (try setClickable(false))
break;
...
default:
break;
}
}
I'm new to Android and I'm building a simple application to start with. It consists of a client with three screens. In the first screen the user is prompted for an Ip to connect to a server (I use an EditText and a button). If the connection is successfully established, some data will be retrieved from the server and the client will show the data on a blank screen (I use a TextView). This would be the second screen. Then, the user could ask the server for detailed information about any data that has been retrieved from the server, which would be the third screen (I use a TextView again).
The problem is that I don't know what's the best way to go about it. I have currently one activity and one XML file containing all the components of the view (EditText, button, TextView). Until now, I've been using setVisibility(View.GONE);to hide certain components depending on the screen the user is in. (For example in the first screen I have to hide both TextViews).
One of the problems I'm facing is that when I put the phone in a horizontal position the components I had hidden show up again. I don't know if hiding views is the ideal thing to do for my purpose.
I've thought that maybe I should use more than one activity, shouldn't I?
I really appreciate any help you can give me to structure my first app.
I would definitely recommend splitting up your App into multiple Activities/Fragments. Depending on how big the logic for each screen gets you will be glad you did it later on because each Activity only has one responsibility.
Look at your mail app for example. You got your List Activity showing you all your mails and then when you select one it starts the Detail Activity showing you the content of your mail. Each Activity is only responsible for one thing which make each one easier to write and maintain.
It also simplifies your layout definitions because each one only contains the relevant parts.
Seems like this is coming up a lot. Android destroys and recreates and Activity when the configuration changes. Screen rotation is part of the orientation. In order to avoid that, the Activity is responsible for retaining state. The mechanisms given for that are the onCreate and onSaveInstanceState. In your example, you could do something like the following:
int uiPhase = 1;
#Override
void onCreate( Bundle data ) {
uiPhase = data.getInt( "uiPhase", 1 );
// inflate layout
setPhase( uiPhase );
}
// invoke the following each time your screen changes
void setPhase( int newPhase ) {
uiPhase = newPhase;
switch( uiPhase ) {
case 1: // show UI elements for first screen, hide others
break;
case 2: // show UI elements for second screen, hide others
break;
case 3: // show UI elements for third screen, hide others
break;
}
}
#Override
void onSaveInstanceState( Bundle data ) {
data.put( "uiPhase", uiPhase );
}
I didn't want to complicate the pattern above too much, but a good method for setting visibility is as follows:
phase1view.setVisibility( uiPhase == 1 ? View.VISIBLE : View.GONE );
phase2view.setVisibility( uiPhase == 2 ? View.VISIBLE : View.GONE );
phase3view.setVisibility( uiPhase == 3 ? View.VISIBLE : View.GONE );
That pulls the repetition in the setPhase method quite a bit together.
Set button visibility to GONE (button will be completely "removed" -- the buttons space will be available for another widgets) or INVISIBLE (button will became "transparent" -- its space will not be available for another widgets):
use in place of
setVisibility(View.GONE)
change to
setVisibility(View.INVISIBLE) and try
I have a Service that sends an Intent to my Activity every 0.1 seconds. I use it to update a custom implementation of a Chronometer. Here everything goes right. The problem comes when I want to update 14 TextView I have in a TableView inside a Fragment in my Activity. Here the app is very slow.
The method in my Activity where it receives the Intent from the Service:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long milis = intent.getLongExtra("milis",0);
if( mFragment != null)
mFragment.Update(milis);
}
};
The code inside the Fragment where I update the TextViews:
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se aumenta la cantidad pasada como parĂ¡metro
for(int i=0;i<7;++i) {
long mCurrentMilis1 = mVectorMilis1.get(i);
long mCurrentMilis2 = mVectorMilis2.get(i);
TextView1 t1 = mListaTitularLayoutLocal.get(i);
TextView1 t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
#Sherif brings up a good point about hidden alpha values that bog down your application a lot.
Depending on your platform you may also want to check
<application android:hardwareAccelerated="true"... />
Another thing you can look into that may help performance is not firing off all those Intents. Once you start firing intents you are getting the system involved and depending on how they are getting resolved it may take some extra time.
For this issue I like to use Handlers. They are more light weight than intent.
You may also want to look at AsyncTask. This is basically like a thread, but also gives hooks that run on the UI Thread so you can perform both perform a background operation and update the UI without have to post runnables.
EDIT: Lastly, you can always run your layouts through the layoutopt tool. I was personally told by Romain Guy himself that if your drawing too slow, than you need to draw less. Just check out a screenshot (from a less than ideal view tree, but well within the max) from the profiling tool. You can see how much of the resources view drawing takes up. It's very important to keep this as lean as possible if you want your app to be responsive.
EDIT: It is no longer called layoutopt, it's called lint. Check your ~/android-sdk/tools/
I have once faced a situation where a fragment was really slow.
I am just predicting that your fragment has some kind of alpha and it is drawn on a 'heavy' activity.
The conclusion is that each time you are setting the text of a textview your whole view hierarchy is being invalidated.
It seems that fragments have this flaw. Anyway, use some layout instead of the fragment and check if it remains 'slow'.
ADDITION: A wrap_content textview will cause much more delay after a setText than a fill_parent textview.
You're likely running into slowdowns due to layout management with TableLayout and TextView. Every time you update text in one of those, a large amount of view measuring has to take place in order to put the characters in the right place on the screen. You should really just profile the app yourself using Traceview to find out. More information at: http://developer.android.com/tools/debugging/debugging-tracing.html
I've had the exact same issue you're seeing with the same type of layout (Fragment > TableLayout > Multiple TextViews). One way to test if your TableLayout/TextView setup is to blame is simply replace all that with a single TextView. That will probably run pretty well. Then put your 14 views into a FrameLayout or RelativeLayout. Even if they all overlap, you should still get decent performance, because it's the complexity of the TableLayout view measurements that's really causing slowdown.
As someone said you can use HardwareAccelerated but this is not a great solution, you will waste ram and cpu if you can't solve it in a different way. A solution probably more safety is to reduce the number of TextView. Try to reduce 14 to 7 and it will go twice faster. Usually is hard to do it but if you put the objects in a strategy position a pair of TextView one above other can be together if you make a TextView with two lines. And don't forget that findViewById is so expensive, if you will use a view object often find it one time and hold its reference.
Benchmarks are always useful for determining where slowness actually comes from, but I feel pretty confident suggesting that sending an Intent is probably much slower than updating 14 TextViews. Sending 10 Intents per second is a sign that you're Doing It Wrong (TM). This is just isn't what they're for.
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
Updating 14 TextViews per second isn't inherently complex; you should be able to easily achieve this with a more appropriate application design. ASyncTask or Handler come to mind as possible tools, but it's hard to know what's best without knowing more about exactly what you're trying to do.
You can try to declare vars outside the loop :
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se
// aumenta la cantidad pasada como parĂ¡metro
long mCurrentMilis1;
long mCurrentMilis2;
TextView1 t1;
TextView1 t2;
for(int i=0;i<7;++i) {
mCurrentMilis1 = mVectorMilis1.get(i);
mCurrentMilis2 = mVectorMilis2.get(i);
t1 = mListaTitularLayoutLocal.get(i);
t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
And to setText() with mixed type, you can try setText("" + milis + mCurrentMilis2);