Keep GridView when resuming from sleep mode - android

I got a problem with a quite large GridView.(about 70 children) The GridView works fine if I start it on onCreate or after resumeing after pressing the home button and then return. But when I resume after coming back from sleep mode, my BaseAdapter starts again and ruin the changes I have done to it during runtime. This also make getChildAt() give a NullPointerException if I am calling it just after restart.
How can I make it just do what regular onPause(home button) does to the GridView, and avoid that the GridView is wiped out everytime I am resumeing from sleep mode?
Edit:
I have tried setting a wakelock for my Activity class that calls the BaseAdpter with no luck
2.Edit: Since I posted this question I have played around with trying to restore the GridView using this code in onPause:
SparseArray<Parcelable> array = new SparseArray<Parcelable>();
gridView.saveHierarchyState(array);
bundle = new Bundle();
bundle.putSparseParcelableArray("state", array);
And this in onResume:
try{
gridView.restoreHierarchyState(bundle.getSparseParcelableArray("state"));
}
catch(Exception e){
//Most likely first start
Log.i("SomeTag", "No GridView state found");
}
}
The strange thing is everything I seems to have jumped from one place to another on the screen and it is still crashing when I try to getChildAt(). It is also failing to get it after sleep mode.
Edit Here is the code from BaseAdapter getView(Note! some of this code is irrelevant)
public View getView (int position, View convertView, ViewGroup parent) {
mParentView = parent;
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
//sets the height for every individual box
int box = width/7*6/10;
ImageCell v = null;
if (convertView == null) {
// If it's not recycled, create a new ImageCell.
v = new ImageCell (mContext);
v.setLayoutParams(new GridView.LayoutParams(box, box));
v.setScaleType(ImageView.ScaleType.CENTER_CROP);
v.setPadding(0, 0, 0, 0);
} else {
v = (ImageCell) convertView;
}
v.mCellNumber = position;
v.mGrid = (GridView) mParentView;
v.mEmpty = true;
int id = 200;
v.setId(++id);
String map = str[position];
int pos = position;
int up = pos-10;
int down = pos+10;
int left = pos-1;
int right = pos+1;
if(up>=0){
above = str[up];
}
else{
//Do nothing
}
if(down<70){
under = str[down];
}
else{
//Do nothing
}
if(left<=-1){
//Do nothing
}
else{
lefte=str[left];
}
if(right>=70){
//Do nothing
}
else{
righte=str[right];
}
//if(left>-1|left!=9|left!=19|left!=29|left!=39|left!=49|left!=59){
// lefte = str[left];
// }
// else{
// Log.i("ImageCellAdapter", "Left is trying to break walls "+left);
//Do nothing
// }
if (map.equals("mountain")) {
//Checks surroundings to find out witch drawable to set
v.setBackgroundResource(R.color.cell_empty);
v.mEmpty = false;
//All
if(above=="mountain"&&under=="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_full);
}
//Single
else if(above=="mountain"&&under!="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up);
}
else if(above!="mountain"&&under!="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left);
}
else if(above!="mountain"&&under!="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_right);
}
//Double
else if(above=="mountain"&&under!="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_up);
}
else if(above=="mountain"&&under!="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_right_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up_right);
}
else if(above!="mountain"&&under!="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_up_down);
}
else if(above=="mountain"&&under=="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up_down);
}
//Triple
else if(above!="mountain"&&under=="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_right_down);
}
else if(above=="mountain"&&under=="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_left_up_down);
}
else if(above=="mountain"&&under!="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_up_right);
}
else if(above=="mountain"&&under=="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_up_right_down);
}
//None
else{
v.setImageResource(R.drawable.mountain);
}
}
else if(map=="start"){
List<String> posOf = Arrays.asList(str);
startPos=posOf.indexOf("start");
v.mEmpty=false;
v.setBackgroundResource(R.color.cell_empty);
getDur();
BitmapDrawable first = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.gress);
BitmapDrawable second =(BitmapDrawable)mContext.getResources().getDrawable(R.drawable.ic_launcher);
BitmapDrawable third = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.gress);
BitmapDrawable fourth = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.ic_launcher);
final AnimationDrawable ani = new AnimationDrawable();
ani.addFrame(first, duration);
ani.addFrame(second, duration);
ani.addFrame(third, duration);
ani.addFrame(fourth, duration);
ani.setOneShot(true);
v.setImageDrawable(ani);
checkIfAnimationDone(ani);
v.post(new Runnable() {
public void run() {
ani.start();
}
});
}
else if(map=="stop"){
v.mEmpty=false;
v.setBackgroundResource(R.color.cell_empty);
v.setImageResource(R.drawable.ic_launcher);
v.setTag(1);
}
else if(map=="grass"){
v.mEmpty=false;
v.setBackgroundResource(R.drawable.gress);
}
else{
// v.setBackgroundResource (R.color.drop_target_enabled);
v.setBackgroundResource (R.color.cell_empty);
}
//v.mGrid.requestDisallowInterceptTouchEvent (true);
//v.setImageResource (R.drawable.hello);
// Set up to relay events to the activity.
// The activity decides which events trigger drag operations.
// Activities like the Android Launcher require a long click to get a drag operation started.
return v;
}
And defining the GridView in onCreate:
gridView= new BoxView(this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH){
w.getDefaultDisplay().getSize(size);
Measuredwidth = size.x;
Measuredheight = size.y;
}else{
Display d = w.getDefaultDisplay();
Measuredwidth = d.getWidth();
Measuredheight = d.getHeight();
}
int width = Measuredwidth/7*6;
gridView.setLayoutParams(new GridView.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
gridView.setNumColumns(columns);
gridView.setVerticalSpacing(0);
gridView.setHorizontalSpacing(0);
gridView.setPadding(0, 0, 0, 0);
gridView.setId(101);
gridView.setSelector(android.R.color.transparent);
gridView.setAdapter (new ImageCellAdapter(this, MAP));

I have noticed one thing in you code:
gridView.setLayoutParams(new GridView.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
Even if GridView is a ViewGroup, you can't access it's LayoutParams. Just think it trough, if this would be possible that means you could put a GridView inside another GridView.
Fix this before going further because is messing with you.
If you want your GridView to be inside a LinearLayout, for example, try this:
gridView.setLayoutParams(new LinearLayout.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
Here is the correct implementation of this answer:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*You have to implement what things from the gridView sould be "saved"*/
GridView gridView = new GridView(this) {
#Override
public Parcelable onSaveInstanceState() {
// Create the Parceable object with the things you want to save
Parceable stateOfGrid = ....etc
return stateOfGrid;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
// Restore your grid's parameters that you previously implemented in onSaveInstanceState
super.onRestoreInstanceState(state);
...
}
};
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
Parcelable state = savedInstanceState.getParcelable("state");
if (state != null) {
gridView.onRestoreInstanceState(state);
Log.d(this.getClass().getName(), "state restored!");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// Put your grid's Parceable into the bundle
super.onSaveInstanceState(outState);
Parcelable state = gridView.onSaveInstanceState();
outState.putParcelable("state", state);
}
}

Try using this tag:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

You can use onSaveInstanceState (Bundle outState)
to save state of your gridview
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable state = gridView.onSaveInstanceState();
outState.putParcelable("state", state);
}
then in onCreate after you seted adapter to grdiview add this code
if (savedInstanceState != null) {
Parcelable state = savedInstanceState.getParcelable("state");
if (state != null) {
gridView.onRestoreInstanceState(state);
Log.d(this.getClass().getName(), "state restored!");
}
}

Is your application running in Landscape Mode?
if yes, then you should consider adding the tag
"android:configChanges="keyboard|keyboardHidden|orientation" for your activity in Manifest.xml, which will prevent android system from killing your activity and restarting it when you unlock the screen.

Related

How to simply save image to photo gallery

I'm playing with a drawing activity in Java converted/decompiled from this Kotlin sample.
I'm simplifying its functionalities and, as it is now, it allows me to click on "Save" button and a preview pops up with a text saying "Saved!", but I'd like to know what needs to be done to simply throw the resulting image to the Android photo gallery anytime the button is clicked (let's say, after saved, the image must become a standalone picture inside the camera gallery).
It seems it has to do with FileOutputStream/Bitmap.CompressFormat/MediaStore.Images
and I can foresee some difficulties in terms of naming files in a way they don't overwrite and I'm reading a lot of answers around here, but I still didn't get the logic so any idea is appreciated.
It's the first time I'm trying to do something similar so I'm sort of lost and I come here to ask for some directions.
Here is the single activity:
public final class SampleActivity extends AppCompatActivity implements OnSeekBarChangeListener, OnClickListener {
private HashMap _$_findViewCache;
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_sample);
(this._$_findCachedViewById(id.close)).setOnClickListener(this);
(this._$_findCachedViewById(id.save)).setOnClickListener(this);
(this._$_findCachedViewById(id.undo)).setOnClickListener(this);
(this._$_findCachedViewById(id.clear)).setOnClickListener(this);
((SeekBar)this._$_findCachedViewById(id.red)).setOnSeekBarChangeListener(this);
((SeekBar)this._$_findCachedViewById(id.green)).setOnSeekBarChangeListener(this);
((SeekBar)this._$_findCachedViewById(id.blue)).setOnSeekBarChangeListener(this);
((SeekBar)this._$_findCachedViewById(id.width)).setOnSeekBarChangeListener(this);
}
public void onProgressChanged(#Nullable SeekBar seekBar, int progress, boolean fromUser) {
int var10000;
SeekBar var10001;
label58: {
label50: {
if (seekBar != null) {
var10000 = seekBar.getId();
var10001 = (SeekBar)this._$_findCachedViewById(id.red);
Intrinsics.checkExpressionValueIsNotNull(var10001, "red");
if (var10000 == var10001.getId()) {
break label50;
}
}
if (seekBar != null) {
var10000 = seekBar.getId();
var10001 = (SeekBar)this._$_findCachedViewById(id.green);
Intrinsics.checkExpressionValueIsNotNull(var10001, "green");
if (var10000 == var10001.getId()) {
break label50;
}
}
if (seekBar == null) {
break label58;
}
var10000 = seekBar.getId();
var10001 = (SeekBar)this._$_findCachedViewById(id.blue);
Intrinsics.checkExpressionValueIsNotNull(var10001, "blue");
if (var10000 != var10001.getId()) {
break label58;
}
}
SeekBar var8 = (SeekBar)this._$_findCachedViewById(id.red);
Intrinsics.checkExpressionValueIsNotNull(var8, "red");
int r = var8.getProgress();
var8 = (SeekBar)this._$_findCachedViewById(id.green);
Intrinsics.checkExpressionValueIsNotNull(var8, "green");
int g = var8.getProgress();
var8 = (SeekBar)this._$_findCachedViewById(id.blue);
Intrinsics.checkExpressionValueIsNotNull(var8, "blue");
int b = var8.getProgress();
int color = Color.argb(255, r, g, b);
((FingerPaintImageView)this._$_findCachedViewById(id.finger)).setStrokeColor(color);
(this._$_findCachedViewById(id.colorPreview)).setBackgroundColor(color);
return;
}
if (seekBar != null) {
var10000 = seekBar.getId();
var10001 = (SeekBar)this._$_findCachedViewById(id.width);
Intrinsics.checkExpressionValueIsNotNull(var10001, "width");
if (var10000 == var10001.getId()) {
((FingerPaintImageView)this._$_findCachedViewById(id.finger)).setStrokeWidth((float)progress);
}
}
}
public void onClick(#Nullable View v) {
if (Intrinsics.areEqual(v, this._$_findCachedViewById(id.undo))) {
((FingerPaintImageView)this._$_findCachedViewById(id.finger)).undo();
} else if (Intrinsics.areEqual(v, this._$_findCachedViewById(id.clear))) {
((FingerPaintImageView)this._$_findCachedViewById(id.finger)).clear();
} else if (Intrinsics.areEqual(v, this._$_findCachedViewById(id.close))) {
this.hidePreview();
} else if (Intrinsics.areEqual(v, this._$_findCachedViewById(id.save))) {
this.showPreview();
}
}
private final void showPreview() {
RelativeLayout var10000 = (RelativeLayout)this._$_findCachedViewById(id.previewContainer);
Intrinsics.checkExpressionValueIsNotNull(var10000, "previewContainer");
var10000.setVisibility(View.VISIBLE);
ImageView var1 = (ImageView)this._$_findCachedViewById(id.preview);
FingerPaintImageView var10001 = (FingerPaintImageView)this._$_findCachedViewById(id.finger);
Intrinsics.checkExpressionValueIsNotNull(var10001, "finger");
var1.setImageDrawable(var10001.getDrawable());
}
private final void hidePreview() {
RelativeLayout var10000 = (RelativeLayout)this._$_findCachedViewById(id.previewContainer);
Intrinsics.checkExpressionValueIsNotNull(var10000, "previewContainer");
var10000.setVisibility(View.GONE);
}
public void onStartTrackingTouch(#Nullable SeekBar seekBar) {
}
public void onStopTrackingTouch(#Nullable SeekBar seekBar) {
}
public void onBackPressed() {
RelativeLayout var10000 = (RelativeLayout)this._$_findCachedViewById(id.previewContainer);
Intrinsics.checkExpressionValueIsNotNull(var10000, "previewContainer");
if (var10000.getVisibility() == View.VISIBLE) {
this.hidePreview();
} else {
super.onBackPressed();
}
}
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(var1);
if (var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(var1, var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if (this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
}
Thanks in advance!
I was able to overcome this issue by taking another paint-like sample (a simpler one and in Java) called Android Drawable View.
This different sample and tips from previous answers available here on StackOverflow like this one and this other one were enough to put the project together so I'll try to explain how to.
First, you need to add permission to WRITE_EXTERNAL_STORAGE in your Manifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Later, you just need to add a save button to your activity_main.xml:
<Button
android:id="#+id/saveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save" />
Then, you initialize the button view onCreate and associate the new saveButton with a setOnClickListener and don't forget to request permission in realtime:
Button saveButton = findViewById(R.id.saveButton);
saveButton.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
if (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED) {
drawableView.setEnabled(true);
}
else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]
{ Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
}
Bitmap bm = drawableView.obtainBitmap();
MediaStore.Images.Media.insertImage(getContentResolver(), bm, "title" , "description");
}
});
By using the method described above, I've been able to save a new media file inside a folder in the default gallery app on the emulator as you can see below:
However, it's still getting an unintended black background that I must overcome now, but I consider the initial issue solved as it answers my own original question.

How to recognize view movement on screen in Android?

I'm working on an SDK for making tutorials. In this SDK I present a spotlight fragment in which I basically create a background canvas there I draw a darker background and a transparent rectangle that focuses on the desired view.
In some cases, this view might move. For example, the developer that uses my SDK creates a timed collapse of a view before the focused view which makes the focused view to move and as a result, my spotlight stays in the wrong location.
The question is: How can I recognize a view movement on the screen so I can update my spotlight fragment accordingly?
The only solution I came up by now is the following 'active' solution, I'm running a Task every half a second that checks the LocationOnScreen of the target view. and if the target view changes it's coordinates I update the fragment. This solution works but I'm still looking for a 'passive' solution that updates me on the location changes instead of testing it every half a second:
#Override
public void onStart() {
super.onStart();
final View targetView = mDrawDataPojo.getWalkthroughMetaPojo().getTargetView().getView();
if (targetView != null) {
targetView.getLocationOnScreen(mOriginalLocationOnScreen);
mTimer = new Timer();
mTimer.schedule(new TargetViewChangeListener(), 0, 500);
}
...
}
#Override
public void onPause() {
super.onPause();
if (mTimer != null) {
mTimer.cancel();
}
...
}
class TargetViewChangeListener extends TimerTask {
public void run() {
int[] currentLocation = new int[2];
mDrawDataPojo.getWalkthroughMetaPojo().getTargetView().getView().getLocationOnScreen(currentLocation);
if (currentLocation[0] != mOriginalLocationOnScreen[0] || currentLocation[1] != mOriginalLocationOnScreen[1]) {
final boolean isActionBar = ABUtils.isActionBarActivity(getActivity());
final int containerId;
try {
mDrawDataPojo.getWalkthroughMetaPojo().setTargetView(new SpotlightTargetView(getActivity(), mDrawDataPojo.getWalkthroughMetaPojo().getTargetView().getView()));
containerId = AndroidUtils.getContainerId(getActivity(), isActionBar);
ABPromotionFragment abPromotionFragment = ABPromotionFragment.newInstance(mDrawDataPojo.getViewDataPojo(), null, mDrawDataPojo.getWalkthroughMetaPojo());
FragmentManager fragmentManager = getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
fragmentTransaction.replace(containerId, abPromotionFragment);
fragmentTransaction.commitAllowingStateLoss();
} catch (Exception e) {
ABLogger.d("TargetViewChangeListener - TimerTask - exception: " + e);
}
}
}
}
Found a much better solution using the OnPreDrawListener:
private final ViewTreeObserver.OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if (!mAttached) {
removePreDrawObserver(null);
return true;
}
handleViewDraw();
return true;
}
};
The onPreDraw method will be called each time the view is going to be redrawn.
Where handleViewDraw method will look like the follows:
private void handleViewDraw() {
if (if mViewAnchor != null) {
View view = mViewAnchor.get();
if (null != view && view.getVisibility() == VISIBLE) {
view.getLocationOnScreen(mTempLocation);
if (mOldLocation == null) {
mOldLocation = new int[]{mTempLocation[0], mTempLocation[1]};
}
if (isTargetViewLocationChanged()) {
handleVisibleTargetViewLocationChange();
}
mOldLocation[0] = mTempLocation[0];
mOldLocation[1] = mTempLocation[1];
} else {
mView.setVisibility(INVISIBLE);
}
} else {
mView.setVisibility(INVISIBLE);
}
}
private boolean isTargetViewLocationChanged() {
Log.d(TAG, "Old: " + mOldLocation[1] + " ,TEMP: " + mTempLocation[1]);
return mOldLocation[0] != mTempLocation[0] || mOldLocation[1] != mTempLocation[1];
}
Using this method you will be notified only when the view moved, in difference with the 'active' solution that is supplied in the other answer this is a 'passive' solution which will run the handleVisibleTargetViewLocationChange method only when the view has actually moved.

Recreate activity passing hardcoded null instance state

I am making a puzzle game and every time the user completes the puzzle, a recreate button appears which is simply calling the recreate() method to restart the puzzle activity.
I override onSaveInstanceState because i want to save the image selected for the puzzle and the 4 pieces in case of screen orientation change.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("originalBM", originalBm);
outState.putParcelable("bm1", bm1);
outState.putParcelable("bm2", bm2);
outState.putParcelable("bm3", bm3);
outState.putParcelable("bm4", bm4);
}
So, when the user clicks the recreate button, the recreate() method is being called which also calls onSaveInstanceState by default because this is how android works and the user will have to play the puzzle with the same image again and again.
I don't want to implement the same code that i have on my onCreate method to select a new random image because this is causing memory leaks and my app crashes after 10-12 recreates.
I simply want it to restart the activity clean and fresh!
Instead of using recreate() inside my recreatePuzzle method, I also also tried this
Intent intent = getIntent();
finish();
startActivity(intent);
But this again is causing my app to crash after 10-12 recreates. It is also causing memory leaks.
So, i believe the best way to do this would be by skipping the Override of saveInstanceState when my recreatePuzzle is being called (if this is possible) or by passing a null Bundle when onSaveInstanceState is being called.
Is there any way to implement any of these solutions above?
Any help would be highly appreciated.
Thank you all in advance.
EDIT:
Full code of my class
package kidsbook.jok.kidsbook;
public class Puzzle extends AppCompatActivity {
private String[] puzzleIMGS;
private String randomPuzzleIMG;
private int corrects = 0, tries = 0;
private ImageView part1, part2, part3, part4;
private TextView piece1, piece2, piece3, piece4;
private Button againButton;
private Bitmap bm1, bm2, bm3, bm4, originalBm;
private Intent i;
private MediaPlayer mp = new MediaPlayer();
private List<Bitmap> parts = new ArrayList<>();
private boolean recreatePuzzle = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_puzzle);
mp = MediaPlayer.create(getApplicationContext(), R.raw.pop);
//Select random image
puzzleIMGS = getResources().getStringArray(R.array.all_animal_imgs);
randomPuzzleIMG = puzzleIMGS[new Random().nextInt(puzzleIMGS.length)];
//Get all elements
againButton = (Button) findViewById(R.id.againPuzzleButton);
part1 = (ImageView) findViewById(R.id.part1);
part2 = (ImageView) findViewById(R.id.part2);
part3 = (ImageView) findViewById(R.id.part3);
part4 = (ImageView) findViewById(R.id.part4);
piece1 = (TextView) findViewById(R.id.piece1);
piece2 = (TextView) findViewById(R.id.piece2);
piece3 = (TextView) findViewById(R.id.piece3);
piece4 = (TextView) findViewById(R.id.piece4);
part1.setOnTouchListener(new MyTouchListener());
part2.setOnTouchListener(new MyTouchListener());
part3.setOnTouchListener(new MyTouchListener());
part4.setOnTouchListener(new MyTouchListener());
piece1.setOnDragListener(new MyDragListener());
piece2.setOnDragListener(new MyDragListener());
piece3.setOnDragListener(new MyDragListener());
piece4.setOnDragListener(new MyDragListener());
if(savedInstanceState!=null) {
Log.i("debug","inside saved instance");
//Convert randomly selected resource image to bitmap
originalBm = savedInstanceState.getParcelable("originalBM");
bm1 = savedInstanceState.getParcelable("bm1");
bm2 = savedInstanceState.getParcelable("bm2");
bm3 = savedInstanceState.getParcelable("bm3");
bm4 = savedInstanceState.getParcelable("bm4");
} else {
Log.i("debug","inside null instance");
//Convert randomly selected resource image to bitmap
originalBm = BitmapFactory.decodeResource(getResources(), getImageId(this, randomPuzzleIMG));
//Split bitmap to 4 parts
bm1 = Bitmap.createBitmap(originalBm, 0, 0, (originalBm.getWidth() / 2), (originalBm.getHeight() / 2));
bm2 = Bitmap.createBitmap(originalBm, (originalBm.getWidth() / 2), 0, (originalBm.getWidth() / 2), (originalBm.getHeight() / 2));
bm3 = Bitmap.createBitmap(originalBm, 0, (originalBm.getHeight() / 2), (originalBm.getWidth() / 2), (originalBm.getHeight() / 2));
bm4 = Bitmap.createBitmap(originalBm, (originalBm.getWidth() / 2), (originalBm.getHeight() / 2), (originalBm.getWidth() / 2), (originalBm.getHeight() / 2));
}
//Make the background transparent
piece1.setBackgroundDrawable(new BitmapDrawable(getResources(), bm1));
piece1.setAlpha(0.2f);
piece2.setBackgroundDrawable(new BitmapDrawable(getResources(), bm2));
piece2.setAlpha(0.2f);
piece3.setBackgroundDrawable(new BitmapDrawable(getResources(), bm3));
piece3.setAlpha(0.2f);
piece4.setBackgroundDrawable(new BitmapDrawable(getResources(), bm4));
piece4.setAlpha(0.2f);
//Place parts in an array
parts.add(bm1);
parts.add(bm2);
parts.add(bm3);
parts.add(bm4);
//Shuffle the array
Collections.shuffle(parts);
//Assign the correct piece tag to each part
for(int i=0;i<4;i++){
if(i==1) {
part1.setImageBitmap(parts.get(i));
if (parts.get(i).equals(bm1)){
part1.setTag("piece1");
} else if (parts.get(i).equals(bm2)){
part1.setTag("piece2");
} else if (parts.get(i).equals(bm3)){
part1.setTag("piece3");
} else {
part1.setTag("piece4");
}
} else if(i==2){
part2.setImageBitmap(parts.get(i));
if (parts.get(i).equals(bm1)){
part2.setTag("piece1");
} else if (parts.get(i).equals(bm2)){
part2.setTag("piece2");
} else if (parts.get(i).equals(bm3)){
part2.setTag("piece3");
} else {
part2.setTag("piece4");
}
} else if(i==3){
part3.setImageBitmap(parts.get(i));
if (parts.get(i).equals(bm1)){
part3.setTag("piece1");
} else if (parts.get(i).equals(bm2)){
part3.setTag("piece2");
} else if (parts.get(i).equals(bm3)){
part3.setTag("piece3");
} else {
part3.setTag("piece4");
}
} else {
part4.setImageBitmap(parts.get(i));
if (parts.get(i).equals(bm1)){
part4.setTag("piece1");
} else if (parts.get(i).equals(bm2)){
part4.setTag("piece2");
} else if (parts.get(i).equals(bm3)){
part4.setTag("piece3");
} else {
part4.setTag("piece4");
}
}
}
}
private static int getImageId(Context context, String imageName) {
return context.getResources().getIdentifier("drawable/" + imageName, null, context.getPackageName());
}
private final class MyTouchListener implements View.OnTouchListener {
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
ClipData data = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, shadowBuilder, view, 0);
return true;
} else {
return false;
}
}
}
class MyDragListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
int action = event.getAction();
switch (action) {
case DragEvent.ACTION_DRAG_STARTED:
// do nothing
break;
case DragEvent.ACTION_DRAG_ENTERED:
break;
case DragEvent.ACTION_DRAG_EXITED:
break;
case DragEvent.ACTION_DROP:
// Dropped, reassign View to ViewGroup
View view = (View) event.getLocalState();
ViewGroup owner = (ViewGroup) view.getParent();
if(view.getTag().equals(v.getTag())){
if(view.getTag().equals("piece1")){
owner.removeView(view);
useMediaPlayer();
piece1.setBackgroundDrawable(new BitmapDrawable(getResources(), bm1));
piece1.setAlpha(0.9f);
corrects++;
} else if (view.getTag().equals("piece2")){
owner.removeView(view);
useMediaPlayer();
piece2.setBackgroundDrawable(new BitmapDrawable(getResources(), bm2));
piece2.setAlpha(0.9f);
corrects++;
} else if (view.getTag().equals("piece3")){
owner.removeView(view);
useMediaPlayer();
piece3.setBackgroundDrawable(new BitmapDrawable(getResources(), bm3));
piece3.setAlpha(0.9f);
corrects++;
} else if (view.getTag().equals("piece4")) {
owner.removeView(view);
useMediaPlayer();
piece4.setBackgroundDrawable(new BitmapDrawable(getResources(), bm4));
piece4.setAlpha(0.9f);
corrects++;
}
}
tries++;
if(corrects==4){
finish();
}
break;
case DragEvent.ACTION_DRAG_ENDED:
break;
default:
break;
}
return true;
}
}
public void useMediaPlayer(){
mp.start();
}
public void againPuzzle(View v){
recreatePuzzle = true;
recreate();
}
public void finish(){
againButton.setVisibility(View.VISIBLE);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("originalBM", originalBm);
outState.putParcelable("bm1", bm1);
outState.putParcelable("bm2", bm2);
outState.putParcelable("bm3", bm3);
outState.putParcelable("bm4", bm4);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_games, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case android.R.id.home:
i = new Intent(this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
return true;
case R.id.menu_about:
i = new Intent(this, About.class);
startActivity(i);
return true;
case R.id.menu_help:
i = new Intent(this, Help.class);
startActivity(i);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onBackPressed() {
i = new Intent(this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
}
What about starting the same activity then finishing the previous one ? like :
Intent intent=new Intent(this, MainActivity.class)
startActivity(intent);
finish();
Using finish will help you not having any memoryleak as oncreate() do.
Second option : move the code selecting a puzzle in a new method (like selectNewPuzzle()), then call that method in onCreated and when you need select a new puzzle.
Third option : use a global boolean named like "canSaveInstance" wich is true when onCreate end. Encapsulate the lines saving the instancestate in an if statement checking this boolean, and when you need recreating, put this variable false, recreate as you usually do (so no data is saved, new puzzle is started) then reput it true (to handle config changes). You will need to be carefull when recreating activity : if(savedinstancestate!=null) must become "if(savedinstancestate!=null && canSaveInstance) (because if not you may try to load data which were not saved previously).
last option : (but i'm not really aware on how to do exactly) prevent user from launching multiple time the puzzle activity, then "launch" a new one (the older one will be overrided i think). You may need do some more research to do this one. As other options are easier I won't search for documentation helping doing that.
So i finally came up with a solution. What i was missing was to release the bitmap memory of my activity and that was what was causing memory leak. So, finally, me recreate() method looks like
public void againPuzzle(View v){
originalBm.recycle();
bm1.recycle();
bm2.recycle();
bm3.recycle();
bm4.recycle();
originalBm = null;
bm1 = null;
bm2 = null;
bm3 = null;
bm4 = null;
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(intent);
}

How to save/Restore state of Views of fragment on Tab change

I am following this tutorial.
there are 3 tabs in my App. in tab3 I m making changes to some views (like buttons and EditText spinners etc) and on the behalf of these changes i have to perform some actions in tab2. Simply you can say that i Change some values in tab3 and effect takes places in tab2. I know how to do this. I just want that my values of view becomes resets every time to default values when switching between the tab2 and tab3
My question is that how can i save the states of my views. so that on resuming the tabs i must get the default look of my views as i had left previously.
One thing more i tell you that i m doing all the work in onCreateView() methos. is this correct way. like this.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Toast.makeText(getActivity(), "onCreateView", Toast.LENGTH_SHORT)
.show();
if (container == null) {
return null;
}
View vi = inflater.inflate(R.layout.settings, container, false);
btnInsert = (Button) vi.findViewById(R.id.btnInsert);
btnInsert.setOnClickListener(this);
btnPosition = (Button) vi.findViewById(R.id.btnPosition);
btnPosition.setOnClickListener(this);
txtPosition = (TextView) vi.findViewById(R.id.txtPosition);
txtLogo = (TextView) vi.findViewById(R.id.txtLogo);
imgLogoPreview = (ImageView) vi.findViewById(R.id.imgLogoPreview);
imgLogoPreview.setOnClickListener(this);
edTxtUserText = (EditText) vi.findViewById(R.id.edTxtPreview);
relLogo = (RelativeLayout) vi.findViewById(R.id.RelLogo);
relText = (RelativeLayout) vi.findViewById(R.id.RelText);
logoWheel = (WheelView) vi.findViewById(R.id.wheelLogo);
logoWheel.setAdapter(new ArrayWheelAdapter<String>(logoWheelList));
logoWheel.setVisibleItems(4);
logoWheel.setCurrentItem(1);
positionWheel = (WheelView) vi.findViewById(R.id.wheelPosition);
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelTextList));
// LogoWheel changed listener
changedListenerLogo = new OnWheelChangedListener() {
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (!wheelScrolled) {
}
}
};
logoWheel.addChangingListener(changedListenerLogo);
// Wheel scrolled listener
scrolledListenerLogo = new OnWheelScrollListener() {
public void onScrollStarts(WheelView wheel) {
wheelScrolled = true;
}
public void onScrollEnds(WheelView wheel) {
wheelScrolled = false;
btnInsert.setText(logoWheelList[wheel.getCurrentItem()] + "");
wheel.setVisibility(View.INVISIBLE);
if (wheel.getCurrentItem() == 2) {
txtPosition.setVisibility(View.INVISIBLE);
btnPosition.setVisibility(View.INVISIBLE);
relText.setVisibility(View.INVISIBLE);
relLogo.setVisibility(View.INVISIBLE);
} else if (wheel.getCurrentItem() == 1) {
relText.setVisibility(View.VISIBLE);
relLogo.setVisibility(View.INVISIBLE);
txtPosition.setVisibility(View.VISIBLE);
btnPosition.setVisibility(View.VISIBLE);
btnPosition.setText("Top");
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelTextList));
positionWheel.setVisibleItems(4);
positionWheel.setCurrentItem(1);
} else if (wheel.getCurrentItem() == 0) {
relLogo.setVisibility(View.VISIBLE);
relText.setVisibility(View.INVISIBLE);
txtPosition.setVisibility(View.VISIBLE);
btnPosition.setVisibility(View.VISIBLE);
btnPosition.setText("Top Left");
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelLogoList));
positionWheel.setVisibleItems(4);
positionWheel.setCurrentItem(1);
}
}
};
logoWheel.addScrollingListener(scrolledListenerLogo);
// /////////////////////Positon Wheel Listners///////////
// LogoWheel changed listener
changedListenerPosition = new OnWheelChangedListener() {
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (!wheelScrolled) {
}
}
};
positionWheel.addChangingListener(changedListenerPosition);
// Wheel scrolled listener
scrolledListenerPosition = new OnWheelScrollListener() {
public void onScrollStarts(WheelView wheel) {
wheelScrolled = true;
}
public void onScrollEnds(WheelView wheel) {
wheelScrolled = false;
String btnStatus = btnInsert.getText().toString();
if (btnStatus.equals("Logo")) {
btnPosition.setText(positionWheelLogoList[positionWheel
.getCurrentItem()] + "");
} else if (btnStatus.equals("Text")) {
btnPosition.setText(positionWheelTextList[positionWheel
.getCurrentItem()] + "");
}
wheel.setVisibility(View.INVISIBLE);
}
};
positionWheel.addScrollingListener(scrolledListenerPosition);
return vi;
}
at what point i must save the states and at what point i should retrieve the savedstates?
Please tell me the how to implement the lifecycle of fragment in simple words.
i also tried the saveInstance() method of fragment. but not called.
Thanks
If I understand you correctly then this might be useful. Instead of recreating Fragments each time you can hide and show them.
This of course preserves your Fragments so is possibly only something you'd do it you had a few tabs. The advantage of this is that
You don't need to worry about saving data and recreating the fragment
Changes are available immediately to the user as soon as the relevant tab is selected.

Android: Button within ListView not receiving onClick events

I am making a date picker activity that looks like a scrolling 30 day month/calendar (think Outlook calendar). The date picker contains a ListView (for scrolling) of MonthView views each of which is a TableView of the individual days. Each individual day in the MonthView is a button. When the MonthView is instantiated I walk each of the days (buttons) and attach a click listener:
final Button b = getButtonAt(i);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
setSelectedDate(buttonDayClosure, b);
}
});
setSelectedDate does a variety of things, but it also turns the button's background to yellow to signify the date is selected.
On my emulator, everything works as you would expect. Activity comes up, you press a day, the day turns yellow. No problems.
However, on some of my peer's emulators and on physical devices when you touch a day nothing happens... until you scroll the ListView... and then all of a sudden the selected day turns yellow. So, for example, you touch "the 3rd" and then nothing happens. Wait a few seconds and then scroll the ListView (touching an area of the calendar that is NOT the 3rd) and as soon as ListView scrolls the 3rd magically turns yellow.
On my peer emulators that show this behavior, I can set a breakpoint on the fist line of onClick and I see that the BP is in fact not hit until the ListView is scrolled.
This behavior doesn't make any sense to me. I would expect the onClick behavior to be unrelated to the encapsulating View's scrolling efforts.
Any thoughts on why this might be the case and how I can rectify the situation so that onClicks always happen immediately when the button is touched?
Thanks.
Post Scriptus: ArrayAdapter and ListView code requested:
public class MonthArrayAdapter extends ArrayAdapter<Date> {
private MonthView[] _views;
private Vector<Procedure<Date>> _dateSelectionChangedListeners = new Vector<Procedure<Date>>();
public MonthArrayAdapter(Context context, int textViewResourceId, Date minSelectableDay, Date maxSelectableDay) {
super(context, textViewResourceId);
int zeroBasedMonth = minSelectableDay.getMonth();
int year = 1900 + minSelectableDay.getYear();
if(minSelectableDay.after(maxSelectableDay))
{
throw new IllegalArgumentException("Min day cannot be after max day.");
}
Date prevDay = minSelectableDay;
int numMonths = 1;
for(Date i = minSelectableDay; !sameDay(i, maxSelectableDay); i = i.addDays(1) )
{
if(i.getMonth() != prevDay.getMonth())
{
numMonths++;
}
prevDay = i;
}
_views = new MonthView[numMonths];
for(int i = 0; i<numMonths; i++)
{
Date monthDate = new Date(new GregorianCalendar(year, zeroBasedMonth, 1, 0, 0).getTimeInMillis());
Date startSunday = findStartSunday(monthDate);
this.add(monthDate);
_views[i] = new MonthView(this.getContext(), startSunday, minSelectableDay, maxSelectableDay);
zeroBasedMonth++;
if(zeroBasedMonth == 12)
{
year++;
zeroBasedMonth = 0;
}
}
for(final MonthView a : _views)
{
a.addSelectedDateChangedListener(new Procedure<MonthView>()
{
#Override
public void execute(MonthView input) {
for(final MonthView b: _views)
{
if(a != b)
{
b.clearCurrentSelection();
}
}
for(Procedure<Date> listener : _dateSelectionChangedListeners)
{
listener.execute(a.getSelectedDate());
}
}
});
}
}
void addSelectedDateChangedListener(Procedure<Date> listener)
{
_dateSelectionChangedListeners.add(listener);
}
private boolean sameDay(Date a, Date b)
{
return a.getYear() == b.getYear() && a.getMonth() == b.getMonth() &&
a.getDate() == b.getDate();
}
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
return _views[position];
}
private Date findStartSunday(Date d)
{
return d.subtractDays(d.getDay());
}
public void setSelectedDate(Date date)
{
for(MonthView mv : _views)
{
mv.setSelectedDate(date);
}
}
}
and
public class DatePicker extends ActivityBase {
public static final String CHOSEN_DATE_RESULT_KEY = "resultKey";
public static final String MIN_SELECTABLE_DAY = DatePicker.class.getName() + "MIN";
public static final String MAX_SELECTABLE_DAY = DatePicker.class.getName() + "MAX";
private static final String SELECTED_DATE = UUID.randomUUID().toString();
private long _selectedDate = -1;
private MonthArrayAdapter _monthArrayAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Date now = new Date();
Bundle inputs = this.getIntent().getExtras();
long min = inputs.getLong(MIN_SELECTABLE_DAY, 0);
Date minSelectableDate;
if(min == 0)
{
minSelectableDate = new Date(now);
}
else
{
minSelectableDate = new Date(min);
}
Log.i(DatePicker.class.getName(), "min date = " + minSelectableDate.toString());
long max = inputs.getLong(MAX_SELECTABLE_DAY, 0);
Date maxSelectableDate;
if(max == 0)
{
maxSelectableDate = new Date(now.addDays(35).getTime());
}
else
{
maxSelectableDate = new Date(max);
}
setContentView(R.layout.date_picker);
Button doneButton = (Button) findViewById(R.id.DatePickerDoneButton);
if(doneButton == null)
{
Log.e(this.getClass().getName(), "Could not find doneButton from view id.");
finish();
return;
}
doneButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
Intent result = new Intent();
result.putExtra(CHOSEN_DATE_RESULT_KEY, _selectedDate);
setResult(RESULT_OK, result);
finish();
}
});
Button cancelButton = (Button) findViewById(R.id.DatePickerCancelButton);
if(cancelButton == null)
{
Log.e(this.getClass().getName(), "Could not find cancelButton from view id.");
finish();
return;
}
cancelButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
setResult(RESULT_CANCELED, null);
finish();
}
});
ListView lv = (ListView)findViewById(R.id.DatePickerMonthListView);
lv.setDividerHeight(0);
_monthArrayAdapter =
new MonthArrayAdapter(this, android.R.layout.simple_list_item_1, minSelectableDate, maxSelectableDate);
_monthArrayAdapter.addSelectedDateChangedListener(new Procedure<Date>()
{
#Override
public void execute(Date input) {
_selectedDate = input.getTime();
}
});
lv.setAdapter(_monthArrayAdapter);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
if(savedInstanceState.containsKey(SELECTED_DATE))
{
_selectedDate = savedInstanceState.getLong(SELECTED_DATE);
_monthArrayAdapter.setSelectedDate(new Date(_selectedDate));
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putLong(SELECTED_DATE, _selectedDate);
}
}
Having the same problem, looking for an answer. I totally didn't believe it when I didn't get my onClick method until I scrolled my list. I'll post the answer here if I find it.
Right now, my best guess is to try different events besides click (because the scroll space is eating the complex touch events that turn into a click event):
"downView" is a static variable to track the element being clicked.
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
downView = v;
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (downView == v) {
handleClick(v);
return true;
}
downView = null;
}
return false;
}
});
The main reason is that ListView doesn't like an adapter having an array of views.
So the problem is triggered by
public View getView (int position, View convertView, ViewGroup parent)
{
return _views[position];
}
When looking at the ListView code (or rather it's parents AbsListView.obtainView method) you'll see code like
if (scrapView != null) {
...
child = mAdapter.getView(position, scrapView, this);
...
if (child != scrapView) {
mRecycler.addScrapView(scrapView);
It can happen that getView(position,...) is called with scrapView != _views[position] and hence scrapView will be recycled. On the other hand, it is quite likely that the same view is also added again to ListView, resulting in views having a weird state (see this issue)
Ultimately, this should be fixed in ListView IMO, but temporarily, I advise against using an adapter containing an array of views.
So I'll add a completely separate answer to this outside of manually composing your own click events from touch events.
I traded some emails with the Android Team (there's a few perks from being consumed by the googly) and they suggested that my attempt to implement ListAdapter by hand was inefficient and that if I don't correctly hook up the data observer methods of the adapter it can cause "funny problems with event handling."
So I did the following:
1) Replaced my implementation of ListAdapter with a subclass of BaseAdapter that overrode the necessary functions.
2) Stopped using list.invalidateViews() and started using adapter.notifyDataChanged()
and the bug seems to have gone away.
That's more work than manually composing a click event, but it's also more correct code in the long run.
Aswer is:
public View getView(int position, View convertView, ViewGroup parent) {
View v=makeMyView(position);
v.setFocusableInTouchMode(false);
return v;
}

Categories

Resources