i have made one of these tap number games for android as a test project and for this i have made a gridview with about 20 buttons in it. when one of these buttons is pressed an animation is started. this runs fine for the first few times but than becomes slower and starts stuttering.
i assume it has something to do with the animation ressource as i use it several times at the same time but i dont know how to solve the problem.
as i want to remove the button from the gridview when the animation ends i wrapped the AnimationDrawable class to be able to set a Handler which is called at the end of the animation.
public void animate() {
setBackgroundResource(R.drawable.myanimation);
final AnimationDrawable anim = (AnimationDrawable) getBackground();
final BetterAnimationDrawable better = new BetterAnimationDrawable(anim);
better.setEndHandler(new EndHandler());
better.start();
}
thanks in advance
UPDATE:
#warpzit: thanks for your answer. it's not a handler for earch click but a handler for each button. the onclick method disables the button (so it can only be pressed once) and then calls animate(). actually theres not much more code i can post, the gridview adapters getView looks like this:
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
final MySpecialButton sb = new MySpecialButton(getApplicationContext());
sb.setOnClickListener(new SpecialButtonClickListener());
return nv;
}
and the mentioned handler looks like this (its actually not removing the button but changing the background-drawable, sorry for that):
private class MySpecialHandler extends Handler {
public MySpecialHandler() {
}
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
final Bitmap bitmapMask = BitmapFactory.decodeResource(getResources(), R.drawable.aspecialmask);
final BitmapDrawable d = new BitmapDrawable(bitmapMask);
d.setColorFilter([someColor], Mode.MULTIPLY);
setBackgroundDrawable(d);
}
};
You make a new handler for each click? That gotta hurt... How about making just 1 for each button or something else far smarter (need to see more code to come with other suggestions).
Related
I'm having a very strange problem. Let me first describe the structure of the views:
MainActivity
CoordinatorLayout
AppBarLayout
CollapsingToolbarLayout
FloatingActionButton
ToolBar
NestedScrollView
TableLayout
TextView (many)
Button (optional)
What I'm trying to do is replacing the one NestedScrollView object with another according to user actions. I have created the following method for this:
public void switchTableGroup(final TableGroup newTG){
final NestedScrollView removeNSV = currentNSV;
Thread switchView = new Thread(){
public void run(){
mainCoordinatorLayout.removeView(removeNSV);
mainCoordinatorLayout.addView(newTG.getNestedScrollView());
}
};
runOnUiThread(switchView);
this.currentTG = newTG;
this.currentNSV = newTG.getNestedScrollView();
}
(note that the TableGroup is a non-graphical object for my internal data structure) This works fine as long as there is at least one Button object in the underlying TableLayout. If there is no Button object present, the old NestedScrollView is removed and the new one is written into currentNSV and added to the CoordinatorLayout, but somehow not invisible. With some dirty code this problem can be solved:
public void switchTableGroupDirty(final TableGroup newTG){
final NestedScrollView removeNSV = currentNSV;
Thread switchView = new Thread(){
public void run(){
mainCoordinatorLayout.removeView(removeNSV);
mainCoordinatorLayout.addView(newTG.getNestedScrollView());
}
};
runOnUiThread(switchView);
this.currentTG = newTG;
this.currentNSV = newTG.getNestedScrollView();
Thread doItAgain = new Thread(){
public void run(){
mainCoordinatorLayout.removeView(newTG.getNestedScrollView());
mainCoordinatorLayout.addView(newTG.getNestedScrollView());
}
};
runOnUiThread(doItAgain);
}
I don't want to depend on doing something twice, is there some specific command to force the CoordinatorLayout to redraw itself? I have already tried invalidate() and refreshDrawableState() on the CoordinatorLayout object, but this didn't help. Also spent a lot of time on trying various combinations, with sleeping etc. You can also imagine it took quite some time to isolate that the presence of the Button was the key factor to the success of the normal method...
Help is greatly appreciated.
In the galleryView onItemSelected I call setText that change the text for a textview thats part of the main layout:
#Override
public void onItemSelected(EcoGalleryAdapterView<?> parent, View view, int position, long id) {
// --- run asyncTask to update gallery view here
TextView myTextView = (TextView) findViewById(R.id.myTextView);
myTextView.setText("position is: ": position);
}
if I left everything as it is and just removed myTextView.setText the gallery works as expected but if I kept it then when scrolling the gallery snaps to the selected position really fast in an ugly way. What could be the issue?
"Ugly" is a pretty subjective term for describing a user interface transition.
However, it sounds as though what you want is a custom animation when an item is selected. onItemSelected() is called before the layout happens, so you can animate your Gallery or individual Views however you want in this method.
I would suggest reading the Animation and Graphics documentation from the Android developer documentation to more fully understand animations and to help decide what you actually want.
The code will vary depending on what you decide you want it to look like and what version of Android you are targeting. A simple View animation that will fade the selected view in might look something like this:
public void onItemSelected(EcoGalleryAdapterView<?> parent, View view,
int position, long id) {
view.setAnimation(new AlphaAnimation(0, 1));
}
// try this
TextView myTextView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_full);
myTextView = (TextView) findViewById(R.id.myTextView);
}
#Override
public void onItemSelected(EcoGalleryAdapterView<?> parent, View view, int position, long id) {
// --- run asyncTask to update gallery view here
myTextView.setText("position is :"+ position);
myTextView.invalidate();
}
The setText commands sits on the UI Thread, maybe it's taking a higher priority or something from the current Gallery animation which disrupts it from acting as it should.
try setting your setText inside a handler:
new Handler().post(new Runnable() {
#Override
public void run() {
myTextView.setText("position is: ": position);
}
});
I have a layout which contains lots of images. What I have to do is when an image is clicked, I have to show its details. But I don't want to have onClickListeners for all the images. How can I achieve this?
You don't have to have different handlers for all the images. Instead use one handler for all the images. This would make your code cleaner, manageable and solve your problem too.
public void onCreate(Bundle bundle) {
//...
OnClickListener mHandler = new OnClickListener() {
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.img1:
//..
break;
case R.id.img2:
//....
break;
}
}
};
ImageButton btn1 = (ImageButton)findViewById(R.id.img1);
ImageButton btn2 = (ImageButton)findViewById(R.id.img2);
//...
btn1.SetOnClickListener(mHandler);
btn2.SetOnClickListener(mHandler);
//...
}
One Listener to rule them all.
Implement onClick() on an object, register it as listener
In onClick(), examine the View object passed as parameter to determine which of the images was clicked. You can do anything from getId() to casting it to (ImageView) and getting the actual image out.
Once you know which image was clicked, do what you will with it.
If you're looking to implement custom behavior for an ImageView (or whatever), and then have multiple instances of that type of view, you should subclass the ImageView and put your listener in there. Then you've got an encapsulated View that implements the custom behavior you want, and if you decide later that you want more or less or them, or to put them in another place, it's easy to move the View and its behavior without ripping apart your Activity.
I'm programming an app that has a grid view with some images, and I'm using the default vertical scroll, since not all the images can be fit into the screen. Alway I do scroll, and there is a new line to show, there seems to be a lag, on the render of the new row.
All the images are drawables, so it is not cause by something like loading the image, at least, not explicitly. So, any ideads of how to solve the problem? The scrolling is lagged in my atrix, and the final devices the app is supposed to run in, are far worse than it.
Edit - Here some code:
public View getView(int position, View convertView, ViewGroup parent) {
T item = getItem(position);
Box box = convertView == null ? new Box(mContext) : (Box) convertView;
onBindModel(box, item);
return box;
}
and
#Override
public void onBindModel(Box box, Model item) {
box.param.putSerializable("model", item);
box.setDescricao(item.getNome());
box.getModelImageView().setImageResource(item.thumbResId);
}
Edit
So, as suggested, just tried this code, but still get the same results:
#Override
public void onBindModel(final Box box, final Model item) {
box.param.putSerializable("model", item);
box.setDescricao(item.getNome());
new Thread(){
public void run() {
Message msg = new Message();
msg.obj =box.getModelImageView();
msg.arg1 =item.thumbResId;
mHandler.sendMessage(msg);
}
}.start();
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message message) {
Bitmap bm = BitmapFactory.decodeResource(getResources(), message.arg1);
setImage((ImageView) message.obj,bm);
}
};
public void setImage(ImageView imgView, Bitmap bm) {
imgView.setImageBitmap(bm);
}
When scroll to the next line I got a hang and then it just continues normally.
Thanks in advance
All the images are drawables, so it is not cause by something like
loading the image, at least, not explicitly.
This is not true. The work is done on the UI thread.
Refer to
http://developer.android.com/reference/android/widget/ImageView.html#setImageResource(int)
You might want to consider lazy loading the images. Multiple approaches have been discussed extensively everywhere. Google for it.
Old question,but it did the trick :
Glide.with(context)
.load(stickersData[position])
.into(imageView);
Using Glide
I have created a bunch of ImageButtons programmatically while in a for loop. They have worked fine as the data displayed in a HorizontalScrollView. Now I need each one to go dim or bright when clicked. First click will setAlpha(45); second click will setAlpha(255);.
I don't think I fully understand how the Views and onClickListener works yet. It seems the onClick function examples I find take a View. How would that function know which button is clicked? Perhaps there is an easier way to do what I want?
Here are the ImageButtons.
TableRow tr0 = new TableRow(this);
tr0.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
for(int but=0; but<ClueList.size(); but++){
ImageButton clueBut = new ImageButton(this);
clueBut.setBackgroundResource(0);
clueBut.setImageBitmap(ClueList.get(but).btmp);
//clueBut.setOnClickListener(this);
tr0.addView(clueBut);
}
Is there something I need to do to make the buttons identifiable? And how would that pass in through into the onClick function to be used?
-: Added Information :-
I am starting to wonder if the problem isn't with the buttons, but with the way I built the screen. More information added.
The Game activity is the main game, which uses the PuzzleView for the upper part of the screen holding the game grid. The lower part is where the ImageButtons are and I built them in place in the Game class.
public class Game extends Activity{
//various variables and stuff
private PuzzleView puzzleView; // The PuzzleView is from another .java file
// public class PuzzleView extends View
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
LinearLayout mainPanel = new LinearLayout(this);
mainPanel.setOrientation(LinearLayout.VERTICLE);
puzzleView = new PuzzleView(this);
mainPanel.addView(puzzleView);
HorizontalScrollView bottom = new HorizontalScrollView(this);
mainPanel.addView(bottom);
TableLayout clues = new TableLayout(this);
bottom.addView(clues);
TableRow tr0 = new TableRow(this);
for(int but=0; but<ClueList.size(); but++){
ImageButton clueBut = new ImageButton(this);
clueBut.setImageBitmap(ClueList.get(but).btmp);
tr0.addView(clueBut);
}
When I try to add the ClickListener(this) I get errors about this not being able to be a Game. I have similar problems in the onClick(View v) function referencing the View. Are these problems because I am building the buttons in the Game Activity instead of a View class?
Thanks
When you set up an OnClickListener and implement the onClick(View v) callback, it's the Dalvik VM the one that will call that method each time the View is clicked, and it will pass the View instance as a parameter. Thus, the code you write inside that method will be applied only to the View that received the click and not to any other View. Add something like this to your loop:
clueBut.setOnClickListener(new OnClickListener() {
public void onClick (View v) {
if (v.getAlpha() == 1f)
v.setAlpha(0.2f);
else
v.setAlpha(1f);
}
});
In the onClick event:
public void onClick(View currentView)
{
Button currentButton = (Button)CurrentView;
//Do whatever you need with that button here.
}
To identify each view uniquely use the property
View. setId(int)
In your case the code would look something like this
for(int but=0; but<ClueList.size(); but++){
ImageButton clueBut = new ImageButton(this);
clueBut.setBackgroundResource(0);
clueBut.setImageBitmap(ClueList.get(but).btmp);
clueBut.setId(but);
//clueBut.setOnClickListener(this);
tr0.addView(clueBut);
}
Inside the onclick listener match the id of the view using findViewByID()