Display animatable instead of text on a button when clicked - android-layout

I am trying to build a button with a progress bar which is displayed in the center of the button and instead of text, as soon as the button is tapped, and while some background processing takes place.
What I am doing is to have a custom button and set the animatable as a drawable left to the button, and in onDraw to move the animatable in the center of the button. However, for a short period of time after the button is tapped, I do not know why there are 2 progress bars on the button.
public class ButtonWithProgressBar extends Button {
protected int progressId = R.drawable.progress_bar;
protected Drawable progress;
protected CharSequence buttonText;
#Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
if (Strings.notEmpty(text)) {
buttonText = text;
}
}
public void startSpinning()
{
if(isSpinning()){
return;
}
progress = getContext().getResources().getDrawable(progressId);
final Drawable[] old = getCompoundDrawables();
setCompoundDrawablesWithIntrinsicBounds(old[0], old[1], progress, old[3]);
setEnabled(false);
if(progress instanceof Animatable){
((Animatable)progress).start();
}
setText("");
}
public void stopSpinning()
{
if(!isSpinning()){
return;
}
if(progress instanceof Animatable){
((Animatable)progress).stop();
}
setEnabled(true);
setText(buttonText);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (progress != null) {
final int drawableHeight = progress.getIntrinsicHeight();
final int drawableWidth = progress.getIntrinsicWidth();
final int drawableTop = (getHeight() - drawableHeight) / 2;
final int drawableLeft = (getWidth() - drawableWidth) / 2;
progress.setBounds(drawableLeft, drawableTop, drawableLeft + drawableWidth, drawableTop + drawableHeight);
progress.draw(canvas);
}
}
}
And this is displayed for a short period after tap, before everything looking as I want:
Any ideas why this is happening?

Found the problem.Updated code to a working version.

Related

TimePicker for each button inside RecyclerView

What I want to accomplish- The recyclerview has a list of apps and each row contains a button. When you click on the button, a timepicker shows up where you choose how long the countdown is for. Then the chosen time appears on the button. When you click the start button, countdowns begin and the time on the button counts down.
I have originally created a button in AppLimitScreen.java and accomplished the above without the button being inside recycler view:
public class AppLimitScreen extends AppCompatActivity {
private int numberOfInstalledApps;
private List<ApplicationInfo> apps;
private ArrayList<AppInfo> res;
//for countdown
static Button chooseTime;
static int Chour, Cminute;
static TimePickerDialog timePickerDialog;
DialogFragment dialogFragment;
private CountDownTimer mCountDownTimer;
static ImageView image;
private Button mButtonStart;
private List<AppLimit> appLimitList = new ArrayList<>();
private RecyclerView recyclerView;
private AppLimitAdapter alAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_limit_screen);
numberOfInstalledApps = getPackageManager().getInstalledApplications(0).size();
apps = getPackageManager().getInstalledApplications(0);
res = new ArrayList<AppInfo>();
mButtonStart = findViewById(R.id.startTimes);
chooseTime = (Button) findViewById(R.id.app_button1);
image = (ImageView) findViewById(R.id.imageView1);
image.setVisibility(View.VISIBLE);
Chour = 0;
Cminute = 0;
chooseTime.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialogFragment = new TimePickerclass();
dialogFragment.show(getSupportFragmentManager(), "Time Picker");
}
});
mButtonStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startTimer();
}
});
updateCountDownText();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
alAdapter = new AppLimitAdapter(appLimitList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(alAdapter);
prepareAppLimitData();
}
private static long START_TIME_IN_MILLIS = 0;
private static long mTimeLeftInMillis = START_TIME_IN_MILLIS;
public static class TimePickerclass extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState){
timePickerDialog = new TimePickerDialog(getActivity(),
AlertDialog.THEME_HOLO_LIGHT,this,Chour,Cminute,true);
// timePickerDialog4.setButton(DialogInterface.BUTTON_POSITIVE, "Start", timePickerDialog4);
return timePickerDialog;
}
public void onTimeSet(TimePicker view, int hourOfDay, int minute){
chooseTime.setText(hourOfDay + "h " + minute + "m" + " 00s");
START_TIME_IN_MILLIS = (hourOfDay * 3600000) + (minute * 60000);
mTimeLeftInMillis = START_TIME_IN_MILLIS;
image.setVisibility(View.INVISIBLE);
}
}
private void updateCountDownText() {
int hours = (int) ((mTimeLeftInMillis / 1000) / 60) / 60;
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
if (minutes > 59) {
minutes = (minutes % 60);
hours += (minutes / 60);
}
String timeLeftFormatted = String.format(Locale.getDefault(), "%02dh %02dm %02ds", hours, minutes, seconds);
chooseTime.setText(timeLeftFormatted);
if (hours != 0 || minutes != 0 || seconds != 0) {
image.setVisibility(View.INVISIBLE);
chooseTime.setClickable(false);
}
else {
image.setVisibility(View.VISIBLE);
chooseTime.setClickable(true);
chooseTime.setText("");
}
}
private void startTimer() {
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {}
}.start();
}
private void prepareAppLimitData() {
AppLimit appLimit;
for (int i = 0; i < apps.size(); i++) {
if (getPackageManager().getLaunchIntentForPackage(apps.get(i).packageName) != null) {
// Non-System App
ApplicationInfo p = apps.get(i);
AppInfo newInfo = new AppInfo();
newInfo.appname = p.loadLabel(getPackageManager()).toString();
newInfo.icon = p.loadIcon(getPackageManager());
res.add(newInfo);
appLimit = new AppLimit(newInfo.appname, newInfo.icon);
appLimitList.add(appLimit);
} else {
// System Apps
}
}
alAdapter.notifyDataSetChanged();
}
}
Then I created the recyclerview and included a button for each row. The code for the AppLimitAdapter is shown below:
public class AppLimitAdapter extends RecyclerView.Adapter<AppLimitAdapter.MyViewHolder> {
private List<AppLimit> appLimitList;
private Context context;
public AppLimitAdapter(Context context) {
this.context = context;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public ImageView icon;
public Button button;
public ImageView image;
public MyViewHolder(View view) {
super(view);
name = (TextView) view.findViewById(R.id.name);
icon = (ImageView) view.findViewById(R.id.icon);
image = (ImageView) view.findViewById(R.id.imageTimer);
button = (Button) view.findViewById(R.id.button);
}
}
public AppLimitAdapter(List<AppLimit> appLimitList) {
this.appLimitList = appLimitList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_limit_list_row, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
AppLimit appLimit = appLimitList.get(position);
holder.name.setText(appLimit.getName());
holder.icon.setImageDrawable(appLimit.getIcon());
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// when each button is clicked, action goes here
}
});
}
#Override
public int getItemCount() {
return appLimitList.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
}
I have tried copying the code from AppLimitScreen.java and placing it inside onClick within onBindViewHolder, but I get errors and I'm sure there's a better way to do this.
How should I implement this?
What I want to accomplish- The recyclerview has a list of apps and each row contains a button. When you click on the button, a timepicker shows up where you choose how long the countdown is for. Then the chosen time appears on the button. When you click the start button, countdowns begin and the time on the button counts down.

How to add more than one same custom view to a grid layout in android from Java code

I am learning custom views in android.
I made one custom view, with a rectangle and a text. The code of the custom view is:
public class TileGenerator extends View {
// rectangle parameters
private Rect rect = new Rect();
private Paint rectPaint = new Paint();
private Integer rectEndX;
private Integer rectEndY;
private Integer rectStartX;
private Integer rectStartY;
private Integer rectFillColor;
private Float rectTextStartX;
private Float rectTextStartY;
//rectangle text
private String rectText;
private Paint rectTextPaint = new Paint();
public TileGenerator(Context context) {
super(context);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void setTileTitleText(String rectText) {
this.rectText = rectText;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
rectEndX = getrectEndX();
rectEndY = getrectEndY();
rectStartX = getRectStartX();
rectStartY = getRectStartY();
rectTextStartX = rectEndX/4f + rectStartX;
rectTextStartY = 3.5f * rectEndY/4f + rectStartY;
rectTextPaint.setTextSize(rectEndY/8);
rectTextPaint.setColor(Color.BLACK);
rect.set(rectStartX,rectStartY,rectEndX,rectEndY);
rectPaint.setColor(getRectFillColor());
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(rect, rectPaint);
canvas.drawText(rectText,rectTextStartX,rectTextStartY,rectTextPaint );
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
public Integer getrectEndX() {
return rectEndX;
}
public void setrectEndX(Integer rectEndX) {
this.rectEndX = rectEndX;
}
public Integer getrectEndY() {
return rectEndY;
}
public void setrectEndY(Integer rectEndY) {
this.rectEndY = rectEndY;
}
public Integer getRectStartX() {
return rectStartX;
}
public void setRectStartX(Integer rectStartX) {
this.rectStartX = rectStartX;
}
public Integer getRectStartY() {
return rectStartY;
}
public void setRectStartY(Integer rectStartY) {
this.rectStartY = rectStartY;
}
public Integer getRectFillColor() {
return rectFillColor;
}
public void setRectFillColor(Integer rectFillColor) {
this.rectFillColor = rectFillColor;
}
public String getRectText() {
return rectText;
}
}
After that I created an blank activity. I am doing all with JAVA code. No XML. Then I try to add above custom view to a gridview layout. I want to add two custom views with different text in a horizontal gridview. So far my code is as below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GridLayout gridLayout = new GridLayout(this);
// first custom view
CustomRectWithText customRectWithText = new CustomRectWithText(this);
customRectWithText.setRectEndX(200);
customRectWithText.setRectEndY(200);
customRectWithText.setRectStartX(2);
customRectWithText.setRectStartY(2);
customRectWithText.setImage(image);
customRectWithText.setRectText("Text");
customRectWithText.setRectFillColor(Color.BLUE);
gridLayout.addView(customRectWithText);
// second custom view
CustomRectWithText customRectWithText1 = new CustomRectWithText(this);
customRectWithText1.setRectEndX(400);
customRectWithText1.setRectEndY(200);
customRectWithText1.setRectStartX(200 + 5);
customRectWithText1.setRectStartY(2);
customRectWithText1.setTileTitleText("Text 1");
customRectWithText1.setRectFillColor(Color.GREEN);
gridLayout.addView(customRectWithText1);
setContentView(gridLayout);
}
But still I am not getting both of the rectangles in a grid view. Only one rectangle is displayed at a time. In above case only first custom view is displayed.
Where am I doing wrong.
All I want is to make a repetitive rectangle of varying labels of any size inside a grid view.
Is this the way to do it. I mean is there any other way around.
I dont want to use ListItems.
Sorry but i do not have enough repo to comment.
But why dont you make an adapter?
Gridview behaves same as listView.
Use adapter to fill your grid.
This is the proper way to populate listView and gridView also.

UIPickerViewModel GetView called infinite times for UIPickerView monotouch

I have a UIPickerView that is using a custom UIPickerViewModel to overide the view that is shown in the UIPicker View.
When I First open the UIPickerView, all goes well, I am able to select an item and what not.
If the Item selected is the first or 2nd item in the UIPicker, I don't run into any issues. However, if I select say the 3rd item, dismiss the Picker then try to select another option again, the UIPickerView never shows on the screen.
What I found out is that the GetView method on my PickerViewModel is getting called over and over again for the selected row as well as the row after. Is there anything I have done that might cause this behavior? Below is my PickerViewModel
public class AddressPickerViewModel : UIPickerViewModel
{
private ShippingView view;
public AddressPickerViewModel(ShippingView view)
{
this.view = view;
}
private AddressPickerCell currentSelectedCell;
public override UIView GetView(UIPickerView picker, int row, int component, UIView view)
{
if (view == null)
{
view = new AddressPickerCell();
var views = NSBundle.MainBundle.LoadNib(AddressPickerCell.Key, view, null);
view = Runtime.GetNSObject(views.ValueAt(0)) as AddressPickerCell;
}
var pickerCell = (AddressPickerCell)view;
pickerCell.Selected = false;
pickerCell.ShippingAddress = this.view.ViewModel.Addresses.ToArray()[row].Address;
return view;
}
public override int GetComponentCount(UIPickerView v)
{
return 1;
}
public override int GetRowsInComponent(UIPickerView pickerView, int component)
{
return this.view.ViewModel.Addresses.Count;
}
public override void Selected(UIPickerView picker, int row, int component)
{
if (this.currentSelectedCell != null)
{
this.currentSelectedCell.Selected = false;
}
view.ViewModel.SelectedAddress=
this.view.ViewModel.Addresses.ElementAt(picker.SelectedRowInComponent(0));
var cell = picker.ViewFor(row, component) as AddressPickerCell;
if (cell != null)
{
this.currentSelectedCell = cell;
this.currentSelectedCell.Selected = true;
}
}
public override float GetComponentWidth(UIPickerView picker, int component)
{
if (component == 0)
return 300f;
else
return 40f;
}
public override float GetRowHeight(UIPickerView picker, int component)
{
return 140f;
}
}

Moving an undecorated stage in javafx 2

I've been trying to move an undecorated stage around the screen, by using the following mouse listeners:
onPressed
onReleased
onDragged
These events are from a rectangle. My idea is to move the undecorated window clicking on the rectangle and dragging all the window.
#FXML
protected void onRectanglePressed(MouseEvent event) {
X = primaryStage.getX() - event.getScreenX();
Y = primaryStage.getY() - event.getScreenY();
}
#FXML
protected void onRectangleReleased(MouseEvent event) {
primaryStage.setX(event.getScreenX());
primaryStage.setY(event.getScreenY());
}
#FXML
protected void onRectangleDragged(MouseEvent event) {
primaryStage.setX(event.getScreenX() + X);
primaryStage.setY(event.getScreenY() + Y);
}
All that I've got with these events is when I press the rectangle and start to drag the window, it moves a little bit. But, when I release the button, the window is moved to where the rectangle is.
Thanks in advance.
I created a sample of an animated clock in an undecorated window which you can drag around.
Relevant code from the sample is:
// allow the clock background to be used to drag the clock around.
final Delta dragDelta = new Delta();
layout.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = stage.getX() - mouseEvent.getScreenX();
dragDelta.y = stage.getY() - mouseEvent.getScreenY();
}
});
layout.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
stage.setX(mouseEvent.getScreenX() + dragDelta.x);
stage.setY(mouseEvent.getScreenY() + dragDelta.y);
}
});
...
// records relative x and y co-ordinates.
class Delta { double x, y; }
Code looks pretty similar to yours, so not quite sure why your code is not working for you.
I'm using this solution for dragging undecoraqted stages by dragging any contained node.
private void addDraggableNode(final Node node) {
node.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
if (me.getButton() != MouseButton.MIDDLE) {
initialX = me.getSceneX();
initialY = me.getSceneY();
}
}
});
node.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
if (me.getButton() != MouseButton.MIDDLE) {
node.getScene().getWindow().setX(me.getScreenX() - initialX);
node.getScene().getWindow().setY(me.getScreenY() - initialY);
}
}
});
}
Based on jewelsea's reply i made two lamba expressions to set the MouseListeners directly on the scene in my start() method. Works fine :)
private double xOffset;
private double yOffset;
/*
The two following lambda expressions makes it possible to move the application without the standard StageStyle
*/
//Lambda mouse event handler
scene.setOnMousePressed(event -> {
xOffset = primaryStage.getX() - event.getScreenX();
yOffset = primaryStage.getY() - event.getScreenY();
});
//Lambda mouse event handler
scene.setOnMouseDragged(event -> {
primaryStage.setX(event.getScreenX() + xOffset);
primaryStage.setY(event.getScreenY() + yOffset);
});enter code here
My guess is that your:
onRectangleReleased()
is passing the very same coordinates from your
onRectanglePressed()
This happens because as stated in the documentation the getX() method from the MouseEvent class returns
Horizontal position of the event relative to the origin of the
MouseEvent's source.
then, when you release your mouse it actually puts your rectangle in the place it was when you first mouse pressed.
BTW, I use a StackPane with a Rectangle inside and then apply the following code:
private void addDragListeners(final Node n){
n.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
if(me.getButton()!=MouseButton.MIDDLE)
{
initialX = me.getSceneX();
initialY = me.getSceneY();
}
else
{
n.getScene().getWindow().centerOnScreen();
initialX = n.getScene().getWindow().getX();
initialY = n.getScene().getWindow().getY();
}
}
});
n.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
if(me.getButton()!=MouseButton.MIDDLE)
{
n.getScene().getWindow().setX( me.getScreenX() - initialX );
n.getScene().getWindow().setY( me.getScreenY() - initialY);
}
}
});
}
addDragListeners(mainStackPane);
Besides that, I use the Middle Mouse Button to center my Window on the screen. I hoped it helped. Cheers!
double x, y;
private void addDragListeners(final Node n, Stage primaryStage){
n.setOnMousePressed((MouseEvent mouseEvent) -> {
this.x = n.getScene().getWindow().getX() - mouseEvent.getScreenX();
this.y = n.getScene().getWindow().getY() - mouseEvent.getScreenY();
});
n.setOnMouseDragged((MouseEvent mouseEvent) -> {
primaryStage.setX(mouseEvent.getScreenX() + this.x);
primaryStage.setY(mouseEvent.getScreenY() + this.y);
});
}
public void start(Stage primaryStage) {
try {
...
addDragListeners(mainPane, primaryStage);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}

LWUIT progress bar

I want to show a progress bar indicating loading application at the beginning.
How can that be done? I have created a gauge but I think it cannot be implemented in LWUIT form..
Based on my comment, You can use progress bar. And also you can use slider component for instead of showing progress bar in LWUIT.
The best way is to use canvas. You can reuse the class in all your apps and it is very efficient. Create a class, say like a class named Splash:
public class Splash extends Canvas {
private final int height;
private final int width;
private int current = 0;
private final int factor;
private final Timer timer = new Timer();
Image AppLogo;
MayApp MIDlet;
/**
*
* #param mainMIDlet
*/
public Splash(MyApp mainMIDlet) {
this.MIDlet = mainMIDlet;
setFullScreenMode(true);
height = getHeight();
width = this.getWidth();
factor = width / 110;
repaint();
timer.schedule(new draw(), 1000, 01);
}
/**
*
* #param g
*/
protected void paint(Graphics g) {
try {//if you want to show your app logo on the splash screen
AppLogo = javax.microedition.lcdui.Image.createImage("/appLogo.png");
} catch (IOException io) {
}
g.drawImage(AppLogo, getWidth() / 2, getHeight() / 2, javax.microedition.lcdui.Graphics.VCENTER | javax.microedition.lcdui.Graphics.HCENTER);
g.setColor(255, 255, 255);
g.setColor(128, 128, 0);//the color for the loading bar
g.fillRect(30, (height / 2) + 100, current, 6);//the thickness of the loading bar, make it thicker by changing 6 to a higher number and vice versa
}
private class draw extends TimerTask {
public void run() {
current = current + factor;
if (current > width - 60) {
timer.cancel();
try {
//go back to your midlet or do something
} catch (IOException ex) {
}
} else {
repaint();
}
Runtime.getRuntime().gc();//cleanup after yourself
}
}
}
and in your MIDlet:
public class MyApp extends MIDlet {
Splash splashScreen = new Splash(this);
public MyApp(){
}
public void startApp(){
try{
Display.init(this);
javax.microedition.lcdui.Display.getDisplay(this).setCurrent(splashScreen);
//and some more stuff
} catch (IOException ex){}
}
//continue

Resources