Is it possible to have a button in a Toast?
In theory, yes because you can build a custom Toast from a layout in XML, but I tried to put a button in it and couldn't get it to register the click.
Did anyone manage to do something like that?
A toast can not be clicked. It is not possible to capture a click inside a toast message.
You will need to build a dialog for that. Look at Creating Dialogs for more info.
The API on the Toast class state that a toast will never receive the focus and because a toast is not a view there is no onClick message. I would assume that therefore childs of a Toast can not be clicked as well.
A toast cant contain a button. Except that the gmail app and the gallery app in jelly beans have a semi toast that contains a button, here is how Google did it
https://gist.github.com/benvd/4090998
I guess this answers your question.
Snippet shows implementation of custom Toast that:
Have similar interface as original Toast class
Can be used as Dialog (have clickable buttons like Gmail app)
Have possibility to set length in millis
Have possibility to set show and cancel animation
Lives only with initialized Activity
Current Limitations:
No screen orientation change are supported
Usage:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
View toastView = new View(getBaseContext());
//init your toast view
ActivityToast toast = new ActivityToast(this, toastView);
//set toast Gravity ( Gravity.BOTTOM | Gravity.FILL_HORIZONTAL by default)
toast.setGravity(Gravity.CENTER);
toast.setLength(10000); //set toast show duration to 10 seconds (2 seconds by default)
Animation showAnim; // init animation
Animation.AnimationListener showAnimListener; //init anim listener
toast.setShowAnimation(showAnim);
toast.setShowAnimationListener(showAnimListener);
Animation cancelAnim; // init animation
Animation.AnimationListener cancelAnimListener; //init anim listener
toast.setCancelAnimation(showAnim);
toast.setCancelAnimationListener(showAnimListener);
toast.show(); //show toast view
toast.isShowing(); // check if toast is showing now
toast.cancel(); //cancel toast view
toast.getView(); //get toast view to update it or to do something ..
}
Sources
import android.app.Activity;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.FrameLayout;
public class ActivityToast {
public static final long LENGTH_SHORT = 2000;
public static final long LENGTH_LONG = 3000;
public static final int DEFAULT_ANIMATION_DURATION = 400;
private final Activity mActivity;
private FrameLayout.LayoutParams mLayoutParams;
private Handler mHandler = new Handler();
private ViewGroup mParent;
private FrameLayout mToastHolder;
private View mToastView;
private Animation mShowAnimation;
private Animation mCancelAnimation;
private long mLength = LENGTH_SHORT;
private Animation.AnimationListener mShowAnimationListener;
private Animation.AnimationListener mCancelAnimationListener;
private boolean mIsAnimationRunning;
private boolean mIsShown;
/**
* #param activity Toast will be shown at top of the widow of this Activity
*/
public ActivityToast(#NonNull Activity activity, View toastView) {
mActivity = activity;
mParent = (ViewGroup) activity.getWindow().getDecorView();
mToastHolder = new FrameLayout(activity.getBaseContext());
mLayoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM | Gravity.FILL_HORIZONTAL
);
mToastHolder.setLayoutParams(mLayoutParams);
mShowAnimation = new AlphaAnimation(0.0f, 1.0f);
mShowAnimation.setDuration(DEFAULT_ANIMATION_DURATION);
mShowAnimation.setAnimationListener(mHiddenShowListener);
mCancelAnimation = new AlphaAnimation(1.0f, 0.0f);
mCancelAnimation.setDuration(DEFAULT_ANIMATION_DURATION);
mCancelAnimation.setAnimationListener(mHiddenCancelListener);
mToastView = toastView;
mToastHolder.addView(mToastView);
mToastHolder.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
cancel();
}
return false;
}
});
}
public void show() {
if (!isShowing()) {
mParent.addView(mToastHolder);
mIsShown = true;
if (mShowAnimation != null) {
mToastHolder.startAnimation(mShowAnimation);
} else {
mHandler.postDelayed(mCancelTask, mLength);
}
}
}
public void cancel() {
if (isShowing() && !mIsAnimationRunning) {
if (mCancelAnimation != null) {
mToastHolder.startAnimation(mCancelAnimation);
} else {
mParent.removeView(mToastHolder);
mHandler.removeCallbacks(mCancelTask);
mIsShown = false;
}
}
}
public boolean isShowing() {
return mIsShown;
}
/**
* Pay attention that Action bars is the part of Activity window
*
* #param gravity Position of view in Activity window
*/
public void setGravity(int gravity) {
mLayoutParams.gravity = gravity;
if (isShowing()) {
mToastHolder.requestLayout();
}
}
public void setShowAnimation(Animation showAnimation) {
mShowAnimation = showAnimation;
}
public void setCancelAnimation(Animation cancelAnimation) {
mCancelAnimation = cancelAnimation;
}
/**
* #param cancelAnimationListener cancel toast animation. Note: you should use this instead of
* Animation.setOnAnimationListener();
*/
public void setCancelAnimationListener(Animation.AnimationListener cancelAnimationListener) {
mCancelAnimationListener = cancelAnimationListener;
}
/**
* #param showAnimationListener show toast animation. Note: you should use this instead of
* Animation.setOnAnimationListener();
*/
public void setShowAnimationListener(Animation.AnimationListener showAnimationListener) {
mShowAnimationListener = showAnimationListener;
}
public void setLength(long length) {
mLength = length;
}
public View getView() {
return mToastView;
}
private Runnable mCancelTask = new Runnable() {
#Override
public void run() {
cancel();
}
};
private Animation.AnimationListener mHiddenShowListener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
if (mShowAnimationListener != null) {
mShowAnimationListener.onAnimationStart(animation);
}
mIsAnimationRunning = true;
}
#Override
public void onAnimationEnd(Animation animation) {
mHandler.postDelayed(mCancelTask, mLength);
if (mShowAnimationListener != null) {
mShowAnimationListener.onAnimationEnd(animation);
}
mIsAnimationRunning = false;
}
#Override
public void onAnimationRepeat(Animation animation) {
if (mShowAnimationListener != null) {
mShowAnimationListener.onAnimationRepeat(animation);
}
}
};
private Animation.AnimationListener mHiddenCancelListener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
if (mCancelAnimationListener != null) {
mCancelAnimationListener.onAnimationStart(animation);
}
mIsAnimationRunning = true;
}
#Override
public void onAnimationEnd(Animation animation) {
mParent.removeView(mToastHolder);
mHandler.removeCallbacks(mCancelTask);
if (mCancelAnimationListener != null) {
mCancelAnimationListener.onAnimationEnd(animation);
}
mIsAnimationRunning = false;
mIsShown = false;
}
#Override
public void onAnimationRepeat(Animation animation) {
if (mCancelAnimationListener != null) {
mCancelAnimationListener.onAnimationRepeat(animation);
}
}
};
}
My original post on github
Post that shows implementation of custom layout in this post
A custom view passed to a toast can contain anything; however, toasts cannot receive any touch events so no components that use touch events will work in a stock toast (buttons, radiobuttons, etc.). The only choice you have is to create a custom view with a button in it and add it to your layout. There are many examples of how to do this and a few libraries you can check out to see how other people are doing it.
UndoBar
MessageBar
Nurik's UndoBar
Of course you are also welcome to use the SuperToasts library I put together however it might be a little overkill for one usage. The way that I do it is outlined in the SuperActivityToast class.
You should use a Snackbar. It is in the latest android support library(at time of answer) and is compatible with older api levels. It is much easier to implement than a Dialog or custom View and has the ability to have a button unlike a Toast.
Download Android Support Library from Extras in the SDK Manager(revision 22.2.1 or later).
In the build.gradle add this to the class dependencies: com.android.support:design:22.2.0.
Implement:
Snackbar.make(this.findViewById(android.R.id.content), "Toast Message", Snackbar.LENGTH_LONG)
.setAction("Click here to activate action", onClickListener)
.setActionTextColor(Color.RED)
.show;
And that is it. No github projects and implementation is very similiar to Toast. I used it in one of my projects and it works great.
You can try SuperToast in this case. It can create toast with button. It has custom duration feature, colourful background, colourful fonts, custom fonts, animated effect. Hope u will enjoy it
Use an alertbox, if you want to add a button :-). Here are some examples
Dialog boxes in Android
Creating a system overlay window (always on top)
This is suggesting that it can be done, I also need buttons in a toast so I still have to make my own implementation. If I find more I will add it to my post
Related
I have a list array adapter.I use a template layout for each elements of "arraylist".I play sound by "onclicklistener" defined on image in my "CustomArrayAdapter.java" class.
by the way i define "onStop()" as override in related activity.
when i click on image,sound plays,but when i test switching activity by hitting home button on mobile,
sound continue playing.
part of my code in Customarraylist.java :
ImageView playIconSecondLanguageImage = (ImageView)list_layout.findViewById(R.id.playIconsecondLanguageImageviewId); playIconSecondLanguageImage.setImageResource(customWord.getmPlayIconImageId();
playIconSecondLanguageImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
releaseMediaPlayer();// to stop another playing when another start instantly
mMediaPlayer =
MediaPlayer.create(v.getContext(),customWord.getmSecondLanguageSoundId());
mMediaPlayer.start();
mOnCompletionListener();
}
});
and part of my code in NumberActivity.java:
}//End of Oncreat
public void releaseMediaPlayer(){
if(mediaPlayer!=null)
{
mediaPlayer.release();
mediaPlayer = null;
}
}
protected void onStop() {
super.onStop();
releaseMediaPlayer();
}
}//End of class NumbersActivity
problem is that mediaplayer in NumbersActivity.java is different whith mediaplayer which i defined in
"Customarraylist.java" to play sound.
my CustomArrayAdapter.java is:
package com.example.customarrayadapter;
import android.app.Activity;
import android.app.Application;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
public class CustomArrayAdapter extends ArrayAdapter<CustomWord> {
// States
private int mColorId;
private MediaPlayer mMediaPlayer;
// to avoid creat new object(here,new medaPlayer), this codes below take out from the Onclick listener
// and define here******************
private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getContext(),"Play finished!",Toast.LENGTH_SHORT).show();
// check is it need to mPlayer anymore or not,to free up memeory usage by the player
releaseMediaPlayer();
}
};
//****************************************
// need Constructor
public CustomArrayAdapter(Activity context, ArrayList<CustomWord>arrayList,int colorId){
super(context,0,arrayList);
this.mColorId = colorId;
}
// use getView override method to move elements of arraylist one by one by its position,to listview
// getView method, steps:
// a. get position of elements in "customWord"(get one from 2 elements in CustomWord" Type,and pass to
// arraylist of type CustomWord,then 2 from 2 elemnts moves),by "position" by "getItem(position)"
// b. prepare a view (convertview),to locate our "list_layout", in it
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
// a.
final CustomWord customWord = getItem(position);
// Define "list_layout" view, to get "convertView" layout in it
View list_layout = convertView;
// if list_layout is null,then put "customlist_layout" in it
if(list_layout==null){
list_layout = LayoutInflater.from(getContext()).inflate(R.layout.customlist_layout,parent,false);
}
// 1 image (source image) + 2 words + 2 image(language flags) + 2 images(olay icon), in customlist_layout.xml so ,so need 2 textview + 1 imageview
// Find the ImageView in the list_item.xml layout with the ID list_item_icon
// ImageView 1 (SourceImage)
ImageView ImageSourceiconView = (ImageView) list_layout.findViewById(R.id.ImageSourceViewId);
// set the image to iconView
ImageSourceiconView.setImageResource(customWord.getmDataResourceId());
// put elements(2 elements) in "CustomWord" one by one into (2 textviews),each textview (one textview to second textview)
// textview 1
TextView secondLanguage_tv = (TextView)list_layout.findViewById(R.id.SecondLanguageId_Textview_ListView);
secondLanguage_tv.setText(customWord.getmSecondLanguageString());
// textview 2
TextView defualtLanguage_tv = (TextView)list_layout.findViewById(R.id.DefualtLanguageId_Textview_ListView);
defualtLanguage_tv.setText(customWord.getmDefualtLanguageString());
// Default Flag Language Image View
// ImageView 2 (Default Flag)
ImageView defaultLanguageFlag = (ImageView) list_layout.findViewById(R.id.DefaultLanguageFlag);
// set the image to iconView
defaultLanguageFlag.setImageResource(customWord.getmDefaultLanguageFlag());
// Second Flag Language ImageView
// ImageView 3 (Second Flag)
ImageView secondLanguageFlag = (ImageView) list_layout.findViewById(R.id.SecondLanguageFlag);
// set the image to iconView
secondLanguageFlag.setImageResource(customWord.getmSecondLanguageFlag());
// source playIcon imageview
ImageView playIconDefualtLanguageImage = (ImageView)list_layout.findViewById(R.id.playIcondefaultLanguageImageViewId);
playIconDefualtLanguageImage.setImageResource(customWord.getmPlayIconImageId());
playIconDefualtLanguageImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
releaseMediaPlayer();// to stop another playing when another start instantly
mMediaPlayer = MediaPlayer.create(v.getContext(),customWord.getmDefaultLanguageSoundId());
mMediaPlayer.start();
mOnCompletionListener();
}
});
// 2nd play icon image set
ImageView playIconSecondLanguageImage = (ImageView)list_layout.findViewById(R.id.playIconsecondLanguageImageviewId);
playIconSecondLanguageImage.setImageResource(customWord.getmPlayIconImageId());
playIconSecondLanguageImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
releaseMediaPlayer();// to stop another playing when another start instantly
mMediaPlayer = MediaPlayer.create(v.getContext(),customWord.getmSecondLanguageSoundId());
mMediaPlayer.start();
mOnCompletionListener();
}
});
// Default Sound Language Image View
// ImageView 4 (Default Language sound)
// set color of layout by mcolorId to sync with name of category in main layout.xml
View textContainer = list_layout.findViewById(R.id.CustomListId);
// ContextCompat is for creat color from getColor
// getColor get color of getContext Object and its color integer id
int color = ContextCompat.getColor(getContext(),mColorId);
textContainer.setBackgroundColor(color);
return list_layout;
}//End of getviw
private void mOnCompletionListener() {
}
public void releaseMediaPlayer(){
if(mMediaPlayer!=null)
{
mMediaPlayer.release();
mMediaPlayer = null;
}
}
public void setmMediaPlayer(MediaPlayer mMediaPlayer) {
this.mMediaPlayer = mMediaPlayer;
}
public MediaPlayer getmMediaPlayer() {
return mMediaPlayer;
}
}
You can achieve your needs using Interface :
Create an Interface and call it (e.g. PlayerCallBack) :
public interface PlayerCallBack {
void onMediaPlayerClickEvent(int position);
}
In your CustomArrayAdapter add a constructor as follows :
public CustomArrayAdapter(Context context, PlayerCallBack playerCallBack, ArrayList<CustomWord> customWords) {
this.context = context;
this.playerCallBack = playerCallBack;
this.customWords = customWords;
}
Change the code of ImageView click listener to this :
playIconSecondLanguageImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playerCallBack.onMediaPlayerClickEvent(position);
}
});
In your NumbersActivity :
CustomArrayAdapter customArrayAdapter = new CustomArrayAdapter(customWords, getApplicationContext(), new PlayerCallBack() {
#Override
public void onMediaPlayerClickEvent(int position) {
mMediaPlayer = MediaPlayer.create(v.getContext(), customWord.getmSecondLanguageSoundId());
mMediaPlayer.start();
}
});
No you can play with MediaPlayer as you want
You have to control activity lifecycles and then you have to switch on/off sound accordingly to your needs.
enter link description here
After googling i found a solution to this.
sound source is playing in "CustomArrayAdapter.java" class that is set in NumbersActivity.java class as Activity. after clicking on image to play sound,sound is playing, when hit "Home" mobile button ,Activating "Activity:NumbersActivity",goes in
onPause then onStop state. but sound is playing.
one way is to find process running in background when "Home" button is hitted.
and that is "com.example.customarrayadapter",so i can find the process and kill it by its id :
codes are written in Activity: NumbersActivity
} //End Of Oncreate
protected void onStop() {
super.onStop();
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> services = manager.getRunningAppProcesses();
String service1name = services.get(0).processName;
int processPid = services.get(0).pid;
Log.d("*Process PID*","**********");
Log.d(activityNameString,"onStop invoked");
Log.d(activityNameString,"Process Id is : "+service1name+"\n");
android.os.Process.killProcess(processPid);
}
}//End of NumbersActivity class
So i have this recycler view in which the last item is supposed to show a dialog on its click. the dialog is further comprising of 2 buttons, positive and negetive( the default ones).
On the press of Positive button, the dialog is supposed to trigger a callback which will add a new entry in the recycler view. On the press of the negetive button, the dialog simply dismisses.
Everything is working fine until when the user clicks positive button. the app crashes with the following log:
2019-08-13 18:27:35.668 29482-29482/z.y.x E/AndroidRuntime: FATAL EXCEPTION: main
Process: z.y.x, PID: 29482
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at z.y.x.dashboard_files.dashboard_fragment.QuantityButtonsAdapter.addItemInCentre(QuantityButtonsAdapter.java:85)
at z.y.x.dashboard_files.dashboard_fragment.DashboardFragment$2$1.onPositiveButtonClick(DashboardFragment.java:144)
at z.y.x.dashboard_files.dashboard_fragment.DashboardFragment$QuantityDialog$3.onClick(DashboardFragment.java:253)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:172)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Here is the custom dialog class:
public static class QuantityDialog {
#Nullable
private QuantityButtonModel currentData;
private AlertDialog.Builder builder;
public interface OnPositiveClickListener {
void onPositiveButtonClick(QuantityButtonModel data);
}
public QuantityDialog(Context ctx) {
currentData = QUANTITY_GLASS;
View dialogView = createView(ctx);
builder = new AlertDialog.Builder(ctx)
.setView(dialogView)
.setCancelable(false)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
}
// public void show() {
// show(null);
// }
#SuppressLint("InflateParams")
private View createView(Context ctx) {
View v = LayoutInflater.from(ctx)
.inflate(R.layout.dialog_new_quantitiy_btn, null);
//init ui
SeekBar seekQty = v.findViewById(R.id.seekbar_qty);
final TextView tvQty = v.findViewById(R.id.tv_qty_text_dialog);
final ImageView ivQty = v.findViewById(R.id.iv_qty_icon_dialog);
//init data and defaults
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
seekQty.setMin(0);
}
seekQty.setMax(QUANTITY_MAX);
if (currentData != null) {
seekQty.setProgress(currentData.getQty());
tvQty.setText(String.format(Locale.ROOT, "%d ml", currentData.getQty()));
ivQty.setImageResource(currentData.getQtyImage());
}
//init listener
seekQty.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
tvQty.setText(String.format(Locale.ROOT, "%d ml", progress));
int resID = getResForQty(progress);
ivQty.setImageResource(resID);
currentData = new QuantityButtonModel(resID, progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return v;
}
public void setOnPositiveClickListener(#Nullable final OnPositiveClickListener listener) {
builder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if (listener != null) {
listener.onPositiveButtonClick(currentData);
}
dialogInterface.dismiss();
}
});
}
public void show() {
builder.show();
}
}
Here I am setting a listener to my recycler view's adapter. the listener is made in a way that if item is at any positon except last, it will show a toast, else it will show a dialog. The dialog is also not being reused(bad naming, i changed it later), but rather getting generated on every click and a click listener being attached at the same point, i.e on rv item's click.
adpButtons.setClickListener(new QuantityButtonClickListener() {
#Override
public void onItemClick(int qty) {
Toast.makeText(
fragView.getContext(),
"add:" + qty + " to shared preferences",
Toast.LENGTH_SHORT)
.show();
}
#Override
public void onAddNewItemClick() {
QuantityDialog reusableQuantityDialog;
reusableQuantityDialog = new QuantityDialog(fragView.getContext());
reusableQuantityDialog.setOnPositiveClickListener(
new OnPositiveClickListener() {
#Override
public void onPositiveButtonClick(QuantityButtonModel data) {
adpButtons.addItemInCentre(data);
}
});
reusableQuantityDialog.show();
}
});
I hope i tried to explain this clearly. I am guessing there is some kind of callback hell that is causing the problem. But i even tried reusing the dialogs which still caused the same error.
Please help.
Edit: Here is the adapter code:
public class QuantityButtonsAdapter extends RecyclerView.Adapter<QuantityButtonsAdapter.RvHolder> {
#NonNull
private List<QuantityButtonModel> buttonModelList;
private QuantityButtonClickListener clickListener;
QuantityButtonsAdapter() {
this(new ArrayList<QuantityButtonModel>(), null);
}
private QuantityButtonsAdapter(#NonNull List<QuantityButtonModel> buttonModelList,
QuantityButtonClickListener listener) {
this.buttonModelList = buttonModelList;
this.clickListener = listener;
}
#NonNull
#Override
public RvHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.layout_recycler_buttons, parent, false);
return new RvHolder(v);
}
#Override
public void onBindViewHolder(#NonNull RvHolder holder, int pos) {
QuantityButtonModel data = buttonModelList.get(pos);
//is last is a check based on which our code to add new data will get triggerred
boolean isLast = (pos==(buttonModelList.size()-1));
holder.bind(data.getQtyImage(), data.getQty(), clickListener, isLast);
}
#Override
public int getItemCount() {
return buttonModelList.size();
}
#NonNull
public List<QuantityButtonModel> getButtonModelList() {
return buttonModelList;
}
public void setButtonModelList(#NonNull List<QuantityButtonModel> buttonModelList) {
this.buttonModelList = buttonModelList;
notifyDataSetChanged();
}
public QuantityButtonClickListener getClickListener() {
return clickListener;
}
public void setClickListener(QuantityButtonClickListener clickListener) {
this.clickListener = clickListener;
notifyDataSetChanged();
}
public void addItemInCentre(QuantityButtonModel model) {
//int pos= buttonModelList.size()/2;
buttonModelList.add(model);
notifyDataSetChanged();
}
class RvHolder extends RecyclerView.ViewHolder {
ImageButton ibtQty;
TextView tvQty;
RvHolder(#NonNull View itemView) {
super(itemView);
ibtQty = itemView.findViewById(R.id.ibt_qty_btn);
tvQty = itemView.findViewById(R.id.tv_qty_text);
//ibtQty.setOnTouchListener(getMyItemTouchListener());
}
void bind(int qtyRes, final int qty, final QuantityButtonClickListener listener, final boolean isLast) {
ibtQty.setImageResource(qtyRes);
tvQty.setText(String.format(Locale.getDefault(), "%d ml", qty));
ibtQty.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isLast) {
listener.onAddNewItemClick();
} else {
listener.onItemClick(qty);
}
showButtonPressAnimation(view);
}
});
}
}
interface QuantityButtonClickListener {
void onItemClick(int qty);
void onAddNewItemClick();
}
public static void showButtonPressAnimation(View view) {
final float shrinkTo = 0.90f;
final long duration = 100;
ScaleAnimation grow = new ScaleAnimation(
shrinkTo, 1,
shrinkTo, 1,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
grow.setDuration(duration / 2);
ScaleAnimation shrink = new ScaleAnimation(
1, shrinkTo,
1, shrinkTo,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
shrink.setDuration(duration / 2);
grow.setStartOffset(duration / 2);
AnimationSet set = new AnimationSet(true);
set.setInterpolator(new LinearInterpolator());
set.addAnimation(shrink);
set.addAnimation(grow);
view.startAnimation(set);
}
}
Didn't read through your Dialog source too much, but I don't expect any issue with it.
I guess the issue is adpButtons.addItemInCentre(data);, which you don't show in the question, but the addItemInCentre implementation is calling List.add on some list instance which is missing the add implementation.
Maybe it's some deserialized list, or some immutable one, can't recall from head any example, but I think there's some which can be created from Strings in XML, etc...
To have add available, you need list instance similar to ArrayList, etc...
So without showing this part of source, how the list (you try to add to) is defined and instantiated, it's difficult to tell precisely, what is the issue.
Edit: it's not clear from OP responses what precisely helped, but among the ways already mentioned above (deserialization, reflection), one can get the List<> instance with fixed size also by using the Arrays.asList(..) utility, which does NOT produce ArrayList, but specialized implementation of List<> which does bridge the original array with List<> interface (you can modify the original array through List<>.set(index, value) call), and thus it does refuse to do add/remove like operations, because underlying classic Type[] array is fixed size and the add/remove then would require new array, invalidating the original reference = makes no sense for asList functionality.
I.e. if you need implementation of java.util.List<E> interface which supports also add and remove like functionality of List, you must make sure the instance of your List<> is provided by class capable of these operations, like java.util.ArrayList<E> or java.util.Vector<E>, and avoid other implementations which do not support add/remove (like the one produced by Arrays.asList(..) call).
Also if you are writing Adapter, where you know you will need add/remove features of List<>, you may sometimes want to enforce particular List implementation, like ArrayList, on the API level, so when you later try to use that adapter with other implementation, it will fail at compile time. (then again having such specific API also prevents usage of other implementations which do support add/remove, and unfortunately the List<E> interface doesn't have direct interface derivative, which would ensure the add/remove are mandatory (they are optional in original List<E> interface), and could be used to narrow the API like that.
Today I'm developing an App which can intercept the launch between activities, My key code is:
ActivityManagerNative.getDefault().setActivityController(new InterceptActivityController(), false);
private class InterceptActivityController extends IWeChatActivityController.Stub {
void InterceptActivityController() {}
#Override
public boolean activityStarting(Intent intent, String pkg) {
showDialog();
return false;
}
}
private void showBottomDialog() {
Log.d(TAG, "showBottomDialog");
Dialog bottomDialog = new Dialog(mContext);
View contentView = LayoutInflater.from(this).inflate(android.R.layout.simple_list_item_2, null);
bottomDialog.setContentView(contentView);
ViewGroup.LayoutParams layoutParams = contentView.getLayoutParams();
layoutParams.width = getResources().getDisplayMetrics().widthPixels;
contentView.setLayoutParams(layoutParams);
bottomDialog.getWindow().setGravity(Gravity.BOTTOM);
bottomDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
bottomDialog.show();
}
I defined a Button and planned to start an Activity after clicking it. But now I intercept this action and just show a dialog in the function of activityStarting and then return false, after dismissing this dialog, I click the button again, but nothing works, dialog doesn't show any more, Who knows the reason ? Maybe I think this is a google source bug, but I'm not sure.
You know the Dialogs need to be Shown in a Timely manner. I mean You need the Dialog to be Shown for How Long? When you Start showing a Dialog and Dismiss it, It's Not Destroyed, It's just Dismissed.
Look at the Code below. I wrote this in my own app, It's Safe. Try it and see if you're satisfied with it:
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bottomDialog.show();
new Thread(new Runnable() {
#Override
public void run() {
try {
for (int i = 0; i <= 1200; i++) {
Thread.sleep(100); //The time it takes to update i
if (i = 1200) {
bottomDialog.dismiss();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
You can use AsyncTask as well. Also, I put the whole thing in a Click Listener just to show how it can be used.
The idea is define a showing Time for the Dialog
I have used code below:
AlertDialog.Builder bld;
if (android.os.Build.VERSION.SDK_INT <= 10) {
//With default theme looks perfect:
bld = new AlertDialog.Builder(AndroidLauncher.this);
} else {
//With Holo theme appears the double Dialog:
bld = new AlertDialog.Builder(AndroidLauncher.this, android.R.style.Theme_Holo_Dialog_MinWidth);
}
bld.setIcon(R.drawable.ic_launcher);
bld.setTitle("Exit");
bld.setMessage("Are you sure you want to exit?");
bld.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); }
});
bld.setPositiveButton("Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) { finish(); }
});
bld.setCancelable(false);
bld.create().show();
It seems fine, but it says "import android.app.AlertDialog cannot resolve".
It is a standard libGDX project in Android Studio.
In libgdx you should use a scene2d dialog instead of the native Android DialogInterface. Below is how you would add a completely skinned dialog to the stage in libgdx with custom button images and background image. You would just need to substitute your own background and button image textures and font, then call quitGameConfirm() when you're ready to display the dialog...
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
public void quitGameConfirm() {
LabelStyle style = new LabelStyle(_fontChat, Color.WHITE);
Label label1 = new Label("Are you sure that you want to exit?", style);
label1.setAlignment(Align.center);
//style.font.setScale(1, -1);
style.fontColor = Color.WHITE;
Skin tileSkin = new Skin();
Texture tex = new Texture(myButtontexture);
tex.setFilter(TextureFilter.Linear, TextureFilter.Linear);
tileSkin.add("white", tex);
tileSkin.add("default", new BitmapFont());
TextButton.TextButtonStyle textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.up = tileSkin.newDrawable("white");
textButtonStyle.down = tileSkin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.checked = tileSkin.newDrawable("white",
Color.LIGHT_GRAY);
textButtonStyle.over = tileSkin.newDrawable("white", Color.LIGHT_GRAY);
textButtonStyle.font = _myTextBitmapFont;
textButtonStyle.font.setScale(1, -1);
textButtonStyle.fontColor = Color.WHITE;
tileSkin.add("default", textButtonStyle);
TextButton btnYes = new TextButton("Exit", tileSkin);
TextButton btnNo = new TextButton("Cancel", tileSkin);
// /////////////////
Skin skinDialog = new Skin(Gdx.files.internal("data/uiskin.json"));
final Dialog dialog = new Dialog("", skinDialog) {
#Override
public float getPrefWidth() {
// force dialog width
// return Gdx.graphics.getWidth() / 2;
return 700f;
}
#Override
public float getPrefHeight() {
// force dialog height
// return Gdx.graphics.getWidth() / 2;
return 400f;
}
};
dialog.setModal(true);
dialog.setMovable(false);
dialog.setResizable(false);
btnYes.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
// Do whatever here for exit button
_parent.changeState("StateMenu");
dialog.hide();
dialog.cancel();
dialog.remove();
return true;
}
});
btnNo.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
//Do whatever here for cancel
dialog.cancel();
dialog.hide();
return true;
}
});
TextureRegion myTex = new TextureRegion(_dialogBackgroundTextureRegion);
myTex.flip(false, true);
myTex.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
Drawable drawable = new TextureRegionDrawable(myTex);
dialog.setBackground(drawable);
float btnSize = 80f;
Table t = new Table();
// t.debug();
dialog.getContentTable().add(label1).padTop(40f);
t.add(btnYes).width(btnSize).height(btnSize);
t.add(btnNo).width(btnSize).height(btnSize);
dialog.getButtonTable().add(t).center().padBottom(80f);
dialog.show(stage).setPosition(
(MyGame.VIRTUAL_WIDTH / 2) - (720 / 2),
(MyGame.VIRTUAL_HEIGHT) - (MyGame.VIRTUAL_HEIGHT - 40));
dialog.setName("quitDialog");
stage.addActor(dialog);
}
The problem is that you are trying to create an Android widget which I suspect you are doing it in the Libgdx-core implementation. The core implementation does not have any references to Android SDK.
That is because it is the Android project which inherits the core project. As a result the core project is not aware of any dependencies loaded to the Android implementation.
To overcome this you need to create an interface between Android project and Core Project. That will allow you to call methods inside the Android Project.
The interface must be created inside the core Project in order for both projects to have access to it.
For example you create CrossPlatformInterface.java inside core Project. But first let's create a callback to get feedback from the Ui Thread inside the Libgdx Thread. It is important to remember that Libgdx has a seperate thread that Android main thread!!! If you try to run Widgets of Android from Libgdx threads the Application will crush.
Let's make the callback for the AlertDialog. I will suggest an Abstract class here in order to be able to override only the methods you want because sometimes Alertdialog can have 1,2 or 3 buttons.
In Core Project create AlertDialogCallback.java:
public abstract class AlertDialogCallback{
public abstract void positiveButtonPressed();
public void negativeButtonPressed(){}; // This will not be required
public void cancelled(){}; // This will not be required
}
In Core Project also create CrossPlatformInterface.java:
public interface CrossPlatformInterface{
public void showAlertDialog(AlertDialogCallback callback);
}
You notice that in the showAlertDialog method we pass the callback to get feedback when buttons are pressed!
Then you create a Class inside Android project that will implement the CrossPlatformInterface like:
public ClassInsideAndroidProject implements CrossPlatFormInterface{
private AndroidLauncher mActivity; // This is the main android activity
public ClassInsideAndroidProject(AndroidLauncher mActivity){
this.mActivity = mActivity;
}
public void showAlertDialog(final AlertDialogCallback callback){
mainActivity.runOnUiThread(new Runnable(){
#Override
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle("Test");
builder.setMessage("Testing");
builder.setPositiveButton("OKAY", new OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
callback.positiveButtonPressed();
}
});
builder.setNegativeButton(negativeButtonString, new OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
callback.negativeButtonPressed();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}
}
Important notes
The CrossPlatformInterface will be instantiated inside the MainActivity (AndroidLauncher) as you will see below.
The AlertDialog will be created inside the android UI thread. Because we are coming from the Libgdx thread to create the AlertDialog we need to use runOnUiThread to ensure the AlertDialog is created in ui thread.
Finally how to execute this:
Instantiate CrossPlatform interface inside Android main Activity and pass the Activity to the Interface instance which is passed inside the MyGdxGame:
public class MainActivity extends AndroidApplication {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
initialize(new MyGdxGame(new ClassInsideAndroidProject(this)), cfg);
}
}
Finally when the MyGDxGame is created we get the instance of the crossplatform interface and we can the call any functions we want to the android ui thread.
public class MyGdxGame extends Game {
ClassInsideAndroidProject crossPlatformInterface;
public MyGdxGame(ClassInsideAndroidProject crossPlatformInterface){
this.crossPlatformInterface = crossPlatformInterface;
}
#Override
public void create() {
crossPlatformInterface.showAlertDialog(new AlertDialogCallback(){
#Override
public void positiveButtonPressed(){
//IMPORTANT TO RUN inside this method the callback from the ui thread because we want everything now to run on libgdx thread! this method ensures that.
Gdx.app.postRunnable(new Runnable().....)
}
#Override
public void negativeButtonPressed(){
}; // This will not be required
#Override
public void cancelled(){
}; // This will not be required
});
}
#Override
public void render() {
super.render();
}
public void dispose() {
super.dispose();
}
public void pause() {
super.pause();
}
}
I think it was much more writing I first intended. It might look daunting but actually is fairly simple. Well after you've done it everything looks simpler :).
The advantage of this effort is after you make this interface any call to android widget will be very easy and thread safe.
Hope it gives a good picture.
This works (tested). Simply pass in the FragmentActivity or Activity
via your game constructor. You have to pass something in (like
ClassInsideAndroidProject). Why not pass in a really useful element !.
//---------------------------------------------------------------------------
/** INSIDE the libgdc core, create a custom NATIVE android dialog
* :- breaks the rules somewhat for the core,
* but if you ONLY using Android, why not use android Native!
* #member_var private final FragmentActivity m_fa;
* #constructor public xx_your_app_xx(FragmentActivity m_fa)
*{
* this.m_fa = m_fa;
*}
* #called_with if(m_fa != null) showCustomDialog(m_fa);
* #param fa
*/
public static void showCustomDialog(final FragmentActivity fa) //or Activity
{
fa.runOnUiThread(new Runnable()
{
// boolean[] info;
#Override
public void run()
{
LinearLayout ll_Main = new LinearLayout(fa);
LinearLayout ll_Row01 = new LinearLayout(fa);
LinearLayout ll_Row02 = new LinearLayout(fa);
LinearLayout ll_Row09 = new LinearLayout(fa);
LinearLayout ll_Row10 = new LinearLayout(fa);
ll_Main.setOrientation(LinearLayout.VERTICAL);
ll_Row01.setOrientation(LinearLayout.HORIZONTAL);
ll_Row02.setOrientation(LinearLayout.HORIZONTAL);
ll_Row09.setOrientation(LinearLayout.HORIZONTAL);
ll_Row10.setOrientation(LinearLayout.HORIZONTAL);
final CheckBox checkBox = new CheckBox(fa);
final CheckBox cb_debug = new CheckBox(fa);
final EditText et_User = new EditText(fa);
final EditText et_Pass = new EditText(fa);
TextView tv_Check = new TextView(fa);
TextView tv_Debug = new TextView(fa);
TextView tv_User = new TextView(fa);
TextView tv_Pass = new TextView(fa);
tv_Check.setText("rotation lock: ");
tv_Debug.setText("debug: ");
tv_User.setText("Username: ");
tv_Pass.setText("Password: ");
ll_Row01.addView(tv_Check);
ll_Row01.addView(checkBox);
ll_Row02.addView(tv_Debug);
ll_Row02.addView(cb_debug);
ll_Row09.addView(tv_User);
ll_Row09.addView(et_User);
ll_Row10.addView(tv_Pass);
ll_Row10.addView(et_Pass);
ll_Main.addView(ll_Row01);
ll_Main.addView(ll_Row02);
// ll_Main.addView(ll_Row09);
// ll_Main.addView(ll_Row10);
AlertDialog.Builder alert = new AlertDialog.Builder(fa);//this.getActivity()
alert.setTitle("Camera settings");
alert.setView(ll_Main);
alert.setCancelable(false);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// info1[0] = checkBox.isChecked();
// info1[1] = cb_debug.isChecked();
// String user = et_User.getText().toString();
// String pass = et_Pass.getText().toString();
//do something with the data
Gdx.app.log("INFO", "**** positiveButtonPressed works here too! ***");
Toast.makeText(fa,
"checkBox: " + checkBox.isChecked() +
", cb_debug: " + cb_debug.isChecked(),
Toast.LENGTH_LONG).show();
//IMPORTANT TO RUN inside this {} means everything now run's on libgdx thread!.
Gdx.app.postRunnable( new Runnable()
{
public void run()
{
//do something with the data
Gdx.app.log("INFO", "**** positiveButtonPressed works here ****");
}//run
});//postRunnable
}//onClick
});//setPositiveButton
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}//setPositiveButton
});//setNegativeButton
AlertDialog dialog = alert.create();
dialog.show();
}//run
});//runOnUiThread
}//showCustomDialog
//--------------------------------------------------------------------------------
I have a countdown timer starting at 60000 milliseconds and want to change the text color from Color.BLUE to Color.RED once the time is at and below 10000 milliseconds. I've tried the following without any success; attempted to setTextColor of TextSwitcher and add IF statement that would change color based on int value timerState.. I can't figure out how to make it work besides possibly stopping the timer and creating another one once the millisecondsUntilFinished hits 10000 which actually lead to my second issue where:
I click on an imageButton that initiates a dialog fragment (PauseFragment) and calling cancel() on my CountDownTimer via timerCDT.cancel(). I ran into some nullpointer issues hence the if statements checking for null in my code, but now once the PauseFragment dismisses my new timer starts back at 60000 rather than where it last left off. I was hoping that long timerState = 60000 would get updated to whatever millisUntilFinished is everytime onTick() was called but I'm not sure where I went wrong!
Therefore, can someone please assist me with changing TextSwitcher text color dynamically and assist in figuring out why my CountDownTimer isn't starting at the expected value. Any assistance is greatly appreciated.
THANKS in advance!
public class GameActivity extends FragmentActivity implements PauseFragment.FragmentCommunicator,{
public static long timerState = 60000;
public static boolean isTimerOn = false;
private String modeChoice = ModesActivity.mode;
private TextSwitcher timerTextSwitcher;
CountDownTimer timerCDT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...more code
timerTextSwitcher = (TextSwitcher) findViewById(R.id.timerTextSwitcher);
timerTextSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
public View makeView() {
// Create a new TextView and set properties
TextView textView = new TextView(getApplicationContext());
textView.setLayoutParams(new TextSwitcher.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
if (timerState < 10001) {
textView.setTextColor(Color.RED);
}
return textView;
}
});
// Declare the animations and initialize them
Animation in = AnimationUtils.loadAnimation(this, android.R.anim.slide_in_left);
Animation out = AnimationUtils.loadAnimation(this, android.R.anim.slide_out_right);
// set the animation type to textSwitcher
timerTextSwitcher.setInAnimation(in);
timerTextSwitcher.setInAnimation(out);
}
timerCDT = new CountDownTimer(timerState, 1000) {
public void onTick(long millisUntilFinished) {
isTimerOn = true;
timerTextSwitcher.setText(String.valueOf(millisUntilFinished / 1000));
timerState = millisUntilFinished;
}
//TODO: assign highscores for players to beat
public void onFinish() {
timerTextSwitcher.post(new Runnable() {
#Override
public void run() {
createToast("GAME OVER!");
}
});
isTimerOn = false;
DialogFragment endDialog = new EndGameFragment();
endDialog.show(getSupportFragmentManager(), "EndGameDialogFragment");
}
};
timerCDT.start();
#Override
public void onPause() {
super.onPause();
Bundle args = new Bundle();
args.putInt(ARG_SCORE, scoreINT);
args.putLong(ARG_TIMER, timerState);
args.putString(GameActivity.ARG_MODE, modeChoice);
if (timerCDT != null) {
timerCDT.cancel();
}
else{
createToastExtended("onPause() - timerCDT is null; attempt to cancel");
}
}
//.!.other fun code here.!.
#Override
protected void onStop() {
super.onStop();
if (timerCDT != null) {
timerCDT.cancel();
}
else{
createToastExtended("onStop() - timerCDT is null; attempt to cancel");
}
}
//Player Response information
#Override
public void pauseFragmentResponse() {
if (timerCDT != null) {
timerCDT.start();
}
else{
createToastExtended("pauseFragmenResponse() - timerCDT is null; attempt to start");
}
}
public void pauseStartFrag(View view) {
DialogFragment dialog = new PauseFragment();
if (timerCDT != null) {
timerCDT.cancel();
}
else{
createToastExtended("pauseStartFrag() - timerCDT is null;attempt to cancel");
}
dialog.show(getSupportFragmentManager(), "PauseDialogFragment");
}
// Code for PauseFragment
//TODO: remove unuses imports on all files within project;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
public class PauseFragment extends DialogFragment {
public static boolean isPaused = false;
public FragmentCommunicator fComm;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
fComm = (FragmentCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragmentCommunicator");
}
}
#Override
public void onDetach() {
super.onDetach();
fComm = null;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
isPaused = true;
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(inflater.inflate(R.layout.fragment_pause, null))
.setMessage(R.string.dialog_pause)
.setPositiveButton(R.string.action_main_menu, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent i4 = new Intent(getActivity(), StartActivity.class);
startActivity(i4);
}
})
.setNeutralButton(R.string.action_restart, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent i4 = new Intent(getActivity(), ModesActivity.class);
startActivity(i4); }
})
.setNegativeButton(R.string.action_resume, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
fComm.pauseFragmentResponse();
dismiss();
}
});
// Create the AlertDialog object and return it
return builder.create();
}
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
isPaused = false;
}
public interface FragmentCommunicator {
public void pauseFragmentResponse();
}
}
Lastly, Idk if it's of any help but I also tried starting the CountDownTimer timerCDT without the FragmentCommunicator interface but the system couldn't find the timer? If someone could shine light on why this happened I'd appreciate it as well.
Seriously, one last thing, if the timer is for a game and needs to be stopped and updated frequently, is it best to use CountDownTimer, TimerTask, a newThread that implements Runnable or a handler or some sort? I've tried them all but as I add components and features to the app I need more and more flexibility with changing the time and not quite sure if I'm headed down the right path. Hope this post isn't too vague. Please let me know if I need to separate into multiple posts or something...
Thanks as always!
just had a look on the developer website here http://developer.android.com/reference/android/os/CountDownTimer.html and it looks like you should probably be placing that if statement in the onTick method, so for every tick you do the check.
EDIT
ok this works perfectly for me
private TextSwitcher TextSw;
private TextView TextSwTextView;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(com.game.test.R.layout.sample);
TextSw = (TextSwitcher) findViewById(R.id.TextSwitchView);
TextSw.setFactory(new ViewSwitcher.ViewFactory()
{
public View makeView()
{
// Create a new TextView and set properties
TextView textView = new TextView(getApplicationContext());
textView.setLayoutParams(new TextSwitcher.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
return textView;
}
});
mtimer = new CountDownTimer(60000, 1000)
{
public void onTick(long millisUntilFinished)
{
TextSwTextView = (TextView) TextSw.getChildAt(0);
if(millisUntilFinished < 10001)
TextSwTextView.setTextColor(Color.RED);
TextSwTextView.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish()
{
TextSwTextView.setText("done!");
}
}.start();
}
so the above is a simple version of yours, the text that is displaying the timer will change to Red when the timer hit 1000. You should be able to build this into yours.
But the main thing you have to do here is to check how much the timer has left in the in the onTick method and also change the text color in here to - see above
This thread helped me solve my problem more easily:
https://groups.google.com/forum/#!topic/android-developers/jdlUp_RlP2w
Your get a handle on the textviews within the textSwitcher like this:
TextView t1 = (TextView) mSwitcher.getChildAt(0);
TextView t2 = (TextView) mSwitcher.getChildAt(1);
Then you set whatever color you need based on your code logic.
TextView t1, t2;
textSwitcher = (TextSwitcher) findViewById(R.id.textView99);
textSwitcher.setInAnimation(this, R.anim.slide_in_right);
textSwitcher.setOutAnimation(this, R.anim.slide_out_left);
t1 = new TextView(this);
t2 = new TextView(this);
t1.setTextSize(20);
t2.setTextSize(20);
textSwitcher.addView(t1);
textSwitcher.addView(t2);