I had been debugged the following code for whole day, and have no idea why it doesn't work as expected.
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.PropertyValuesHolder;
import com.nineoldandroids.animation.ValueAnimator;
private void animateThumbCoorToMatchCurrentIndixes() {
float newThumbLeftCoor = this.indexToCoor(this.thumbLeftIndex);
float newThumbRightCoor = this.indexToCoor(this.thumbRightIndex);
PropertyValuesHolder thumbLeftPropertyValuesHolder = PropertyValuesHolder.ofFloat("thumbLeftCoor", this.thumbLeftCoor, newThumbLeftCoor);
PropertyValuesHolder thumbRightPropertyValuesHolder = PropertyValuesHolder.ofFloat("thumbRightCoor", this.thumbRightCoor, newThumbRightCoor);
Log.i("CHEOK", "Animate " + this.thumbLeftCoor + " -> " + newThumbLeftCoor);
Log.i("CHEOK", "Animate " + this.thumbRightCoor + " -> " + newThumbRightCoor);
ValueAnimator valueAnimator = ObjectAnimator.ofPropertyValuesHolder(this, thumbLeftPropertyValuesHolder, thumbRightPropertyValuesHolder);
valueAnimator.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime));
valueAnimator.setRepeatCount(0);
valueAnimator.setInterpolator(new DecelerateInterpolator());
Log.i("CHEOK", "Animation duration = " + valueAnimator.getDuration());
valueAnimator.start();
}
// Do not remove this code. It is used for reflection call for animation.
#SuppressWarnings("unused")
private void setThumbLeftCoor(float thumbLeftCoor) {
this.thumbLeftCoor = thumbLeftCoor;
Log.i("CHEOK", "SET thumbLeftCoor " + thumbLeftCoor);
this.postInvalidate();
}
// Do not remove this code. It is used for reflection call for animation.
#SuppressWarnings("unused")
private void setThumbRightCoor(float thumbRightCoor) {
this.thumbRightCoor = thumbRightCoor;
Log.i("CHEOK", "SET thumbRightCoor " + thumbRightCoor);
this.postInvalidate();
}
My console log is as follow :
Animate 636.0985 -> 0.0
Animate 679.27057 -> 578.0
Animation duration = 200
However, methods setThumbLeftCoor and setThumbRightCoor never get triggered.
Anyone have idea why it happens so?
All the while, by having setThumbLeftCoor and setThumbRightCoor as private method work for my case.
Just that recently, a child class is being derived from the above class.
To make the above code work as it is, changing setThumbLeftCoor and setThumbRightCoor to public method is required. (Even protected won't work)
My guess is, this is due to limitation of "reflection"
Related
Hi I have been quite struggling with this for a while. Any help is appreciated.
I have a requirement to run one observable after completion of another observable. So e.g. The following code creates an observable from input value to value + 10.
Observable<ColoredIntegerModel> getSequenceObservable(int value, int delay, int color) {
return Observable.range(value,10)
.map(i -> {
Log.d(TAG, "Value " + i
+ " evaluating on " + Thread.currentThread().getName()
+ " emitting item at " + System.currentTimeMillis());
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
return new ColoredIntegerModel(i, color);
});
}
The ColorIntegerModel is as follows
public class ColoredIntegerModel {
private Integer mValue;
private int mColor;
public ColoredIntegerModel(Integer value, int color) {
mValue = value;
mColor = color;
}
public Integer getValue() {
return mValue;
}
public int getColor() {
return mColor;
}
}
I create the two observables as follows and concat them like so .
Observable<ColoredIntegerModel> observable1 = getSequenceObservable(1, 1000, Color.BLUE);
Observable<ColoredIntegerModel> observable11 = getSequenceObservable(11, 1000, Color.RED);
mDisposable =
observable1.concatWith(observable11)
.doOnDispose(() -> {Log.d(TAG, "observable disposed");})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.repeat(2)
.subscribe((m) -> {
Utils.appendColoredText(mResultTextView, "Adding item "
+ m.getValue().toString() + "\n", m.getColor());
});
The above code prints 1..10 (in blue each item delayed by 1s) and 11..20 (in red).
So far so good.
But my requirement is to create the second observable only after the first is complete. Infact it could be array of observables, where the n+1 observable is only created after the nth is done. Each observable can emit multiple items. Is there any operator to achieve this?
I don't know if I understood it right but if you want to create the Observable when you subscribe to it you need the defer operator
You can start next observable from doOnCompleted() of previous one
Observable<MyData> observable1 = ...;
Observable<MyData> observable2 = ...;
Observable<MyData> observable3 = ...;
Observable
.concat(observable1.doOnCompleted(this::onCompleteObservable1),
observable2.doOnCompleted(this::onCompleteObservable2),
observable3.doOnCompleted(this::onCompleteObservable3))
...
...
Hope this help.
I've try to animate int value like this
intAnim = ValueAnimator.OfInt(0, 100);
intAnim.AddUpdateListener(this);
intAnim.SetDuration(450);
intAnim.Start();
And write it in console while updating
public void OnAnimationUpdate(ValueAnimator animation)
{
Console.WriteLine("e.Animation.CurrentPlayTime " + animation.CurrentPlayTime + "/ " + animation.Duration);
Console.WriteLine("e.Animation.AnimatedValue " + animation.AnimatedValue);
}
The value does not update from start value it's always set to target value so message in console will be like
e.Animation.CurrentPlayTime 0/ 450
e.Animation.AnimatedValue 100
Does not sure what is wrong..
I implemented ItemDecoration into my RecyclerView along with an Animation that plays whenever the RV is loaded. However, I noticed that the decoration appears already at the bounds before the animation completes, and I want to have the decoration move in with the animation at the same time. How would I do this?
So far, I have been entering the animation like so:
#Override
public void onBindViewHolder(final RecyclerVH recyclerVH, final int position) {
currentNote = data.get(position);
final String currentTitle = currentNote.getTitle();
final String currentContent = currentNote.getContent();
final int currentPosition = currentNote.getPosition();
String currentAlarmDate = currentNote.getAlarm();
Log.d("RecyclerView", "onBindVH called: " + currentTitle);
Log.d("RecyclerView", "Position at: " + currentPosition + " and Adapter Position at: " + recyclerVH.getAdapterPosition());
// final Info currentObject = data.get(position);
// Current Info object retrieved for current RecyclerView item - USED FOR DELETE
recyclerVH.listTitle.setText(currentTitle);
recyclerVH.listContent.setText(currentContent);
Log.d("RecyclerAdapter", "currentAlarmDate is: '" + currentAlarmDate + "'");
if (currentAlarmDate != null && !currentAlarmDate.equals(" ")) {
Log.d("RecyclerAdapter", "Current Alarm set for: " + currentAlarmDate);
recyclerVH.alarm.setText(currentAlarmDate);
} else
recyclerVH.alarm.setText(R.string.no_alarm);
/*recyclerVH.listTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Open new Activity containing note content
Toast.makeText(this, "Opening: " + currentObject.title, Toast.LENGTH_LONG).show();
}
});*/
runEnterAnimation(recyclerVH.itemView, position);
}
private void runEnterAnimation(View itemView, int position) {
// Starts Animation
Animation animation = AnimationUtils.loadAnimation(context,
position >= lastAnimatedPosition ? R.anim.up_from_bottom : R.anim.down_from_top);
// If true, up_from_button is loaded
itemView.startAnimation(animation);
lastAnimatedPosition = position;
Log.d("RecyclerAdapter", "lastAnimatedPosition is now: " + lastAnimatedPosition);
}
Seems like #Mimmo Grottoli's answer is the closest thing to a solution. Check out this post: How to disable RecyclerView item decoration drawing for the duration of item animations
You can read the documentation
Or if need to animated decoration you can try this:
https://github.com/slidenerd/materialtest
Thanks, if you not get your solution from it than please ask me i will try my best to give you a better solution. :)
I am extending a DialogPreference to get the following Dialog:
I want it to be able to pick two times: a starting time and an ending time (for whatever reason). For this the TimePicker should always show the time on the ToggleButton that is checked.
In order to let the user know which of both times he is currently changing, I use ToggleButtons and ensure by OnCheckedChangeListeners that exactly one is checked all the time. No problems so far. The same listeners are used to change the text on those ToggleButtons (by both, the setTextOn/setTextOff, methods).
See the following code I use in the extended DialogPreference:
/*
* Bind data to our content views
* */
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
// use 24h format
mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
mTimePicker.setIs24HourView(true);
mToggleButtonFrom = (ToggleButton) view.findViewById(R.id.toggleButtonDisplayOnFrom);
mToggleButtonUntil = (ToggleButton) view.findViewById(R.id.toggleButtonDisplayOnUntil);
// save the time of the timepicker internally for both buttons, if there are no saved values
if(fromHour == -1 && fromMin == -1) {
fromHour = mTimePicker.getCurrentHour();
fromMin = mTimePicker.getCurrentMinute();
}
if(untilMin == -1 && untilHour == -1){
untilHour = mTimePicker.getCurrentHour();
untilMin = mTimePicker.getCurrentMinute();
}
// set currently saved times
mToggleButtonFrom.setTextOn(String.format("%02d", fromHour) + ":" + String.format("%02d", fromMin));
mToggleButtonFrom.setTextOff(String.format("%02d", fromHour) + ":" + String.format("%02d", fromMin));
mToggleButtonUntil.setTextOn(String.format("%02d", untilHour) + ":" + String.format("%02d", untilMin));
mToggleButtonUntil.setTextOff(String.format("%02d", untilHour) + ":" + String.format("%02d", untilMin));
// update the button (not working without this)
mToggleButtonFrom.setChecked(mToggleButtonFrom.isChecked());
mToggleButtonUntil.setChecked(mToggleButtonUntil.isChecked());
// set togglebuttons to exclude each other
mToggleButtonFrom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// The toggle is enabled
if (mToggleButtonUntil.isChecked())
mToggleButtonUntil.setChecked(false);
} else {
// The toggle is disabled
if (!mToggleButtonUntil.isChecked())
mToggleButtonUntil.setChecked(true);
fromHour = getCurrentlyPickedHour2();
fromMin = getCurrentlyPickedMinute2();
String time = getStringTime(fromHour, fromMin);
mToggleButtonFrom.setTextOff(time);
mToggleButtonFrom.setTextOn(time);
setTimeToTimePickerSlim(false);
}
}
});
mToggleButtonUntil.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// The toggle is enabled
if (mToggleButtonFrom.isChecked())
mToggleButtonFrom.setChecked(false);
} else {
// The toggle is disabled
if (!mToggleButtonFrom.isChecked())
mToggleButtonFrom.setChecked(true);
untilHour = getCurrentlyPickedHour2();
untilMin = getCurrentlyPickedMinute2();
String time = getStringTime(untilHour, untilMin);
mToggleButtonUntil.setTextOff(time);
mToggleButtonUntil.setTextOn(time);
setTimeToTimePickerSlim(true);
}
}
});
}
private void setTimeToTimePickerSlim(boolean from){
if(from){
mTimePicker.setCurrentHour(Integer.valueOf(fromHour));
mTimePicker.setCurrentMinute(Integer.valueOf(fromMin));
} else {
mTimePicker.setCurrentHour(Integer.valueOf(untilHour));
mTimePicker.setCurrentMinute(Integer.valueOf(untilMin));
}
}
private int fromHour = -1;
private int fromMin = -1;
private int untilHour = -1;
private int untilMin = -1;
private int getCurrentlyPickedHour2(){
return mTimePicker.getCurrentHour();
}
private int getCurrentlyPickedMinute2(){
return mTimePicker.getCurrentMinute();
}
private String getStringTime(int hour, int min){
return String.format("%02d", hour)+":"+String.format("%02d", min);
}
I have absolutly no clue, why the TimePicker is not updating to the shown (and set) time, when I choose a new time and then click on one of those ToggleButtons (same behaviour for both of them as far as I can tell).
But the most interesting part comes now: When I do this twice (choose a new time, toggle the buttons, choose another time, toggle again), the TimePicker starts toggling the shown time, too. Exactly what I want it to show!
I tried a lot of things, here are my 'best' guesses:
If I change the method setTimeToTimePickerSlim(boolean) to
private void setTimeToTimePickerSlim(boolean from){
if(from){
mTimePicker.setCurrentHour(3);
mTimePicker.setCurrentMinute(15);
} else {
mTimePicker.setCurrentHour(21);
mTimePicker.setCurrentMinute(45);
}
}
this works like intended (but is of cause a bit to static to be usefull ..). Unfortunately removing the Integer.valueOf() does not help, too.
I would be very happy for any hint (since I am trying to debug this for hours now).
Update: I logged the values by the following modification
private void setTimeToTimePickerSlim(boolean from){
Log.i("StrangeBugTest", "Before setting: from: " + fromHour + ":" + fromMin + " until: " + untilHour + ":" + untilMin + " (from=" + from + ")");
Log.i("StrangeBugTest", "Pickertime: " + mTimePicker.getCurrentHour() + ":" + mTimePicker.getCurrentMinute());
if(from){
mTimePicker.setCurrentHour(fromHour);
mTimePicker.setCurrentMinute(fromMin);
} else {
mTimePicker.setCurrentHour(untilHour);
mTimePicker.setCurrentMinute(untilMin);
}
Log.i("StrangeBugTest", "After setting: from: " + fromHour + ":" + fromMin + " until: " + untilHour + ":" + untilMin + " (from=" + from + ")");
Log.i("StrangeBugTest", "Pickertime: " + mTimePicker.getCurrentHour() + ":" + mTimePicker.getCurrentMinute());
}
When I start the app (and open the preferenceDialog) I get the following screen:
I than picked the hour 21 (because I am using 24h-format) and minute 45. After this i clicked the toggleButtonFrom (on the left). This gets me the following log (shortened):
10-20 19:43:11.575 Before setting: from: 21:45 until: 19:42 (from=false)
10-20 19:43:11.575 Pickertime: 21:45
10-20 19:43:11.575 After setting: from: 21:45 until: 19:42 (from=false)
10-20 19:43:11.575 Pickertime: 21:45
This is wrong, because the pickertime should be 19:42 after the setting. However, the gui is consistent to the picked time:
I than changed the Picker back to hour, chose hour 15 followed by minute 15 and clicked on ToggleButtonUntil (right one). This gives me the following log
10-20 19:44:26.178 Before setting: from: 21:45 until: 15:15 (from=true)
10-20 19:44:26.178 Pickertime: 15:15
10-20 19:44:26.188 After setting: from: 21:45 until: 15:15 (from=true)
10-20 19:44:26.188 Pickertime: 21:45
This is how it should work from the very beginning! The GUI shows:
From here on, everything is fine, both, times and buttons toggle as intended. But I do not see, why the first one does not work. I would be happy for every hint I can get.
I trying to build a app on beacons. so I got a sample code from here.
I have run that code and as soon as I entered into range of beacon the app is crashed and error in the logcat is as follow:
06-20 17:48:24.256: E/AndroidRuntime(18104): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Please help me out.
I checked the code and the error lies in between these lines.
iBeaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons, Region region) {
if (iBeacons.size() > 0) {
double distance = iBeacons.iterator().next().getAccuracy();
DecimalFormat decimalFormat = new DecimalFormat("##.##");
double distanceFormatted = Double.valueOf(decimalFormat.format(distance));
TextView distanceTextView = (TextView) findViewById(R.id.am_tv_distance);
distanceTextView.setText(String.valueOf(distanceFormatted) + " m");
}
}
});
You need to perform UI changes in the UI thread:
iBeaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons, Region region) {
if (iBeacons.size() > 0) {
double distance = iBeacons.iterator().next().getAccuracy();
DecimalFormat decimalFormat = new DecimalFormat("##.##");
double distanceFormatted = Double.valueOf(decimalFormat.format(distance));
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView distanceTextView = (TextView) findViewById(R.id.am_tv_distance);
distanceTextView.setText(String.valueOf(distanceFormatted) + " m");
}
});
}
}
});
For the CalledFromWrongThreadException,
write a function and call that like:
private void toastToDisplay(final String line) {
runOnUiThread(new Runnable() {
public void run() {
//Toast.makeText(YourActivity.this, "" + line,
// Toast.LENGTH_LONG).show();
TextView yourTextView = (TextView) findViewById(R.id.your_tv);
yourTextView .setText(line);
}
});
}
You can edit that function with your required distanceTextView or other views if you want.
What happens there is that the Service is running on the background thread in your application, so when you directly call an operation that would update the UI, it conflicts with the thread you are in while writing that code.
So If the current thread is not the UI thread, the action is posted to the event queue of the UI thread by using runOnUiThread
What exactly is the meaning of "I am getting only distance not range" ?
...for the iBeacons.iterator().next().getAccuracy();, part.. you can collect the beacon details by using a for each loop like:
for (IBeacon iBeacon : iBeacons) {
String line = "Major: " + iBeacon.getMajor()
+ "Minor: " + iBeacon.getMinor()
+ "Accuracy in m(s): "
+ iBeacon.getAccuracy()
+ "BluetoothAddress: "
+ iBeacon.getBluetoothAddress()
+ "Proximity: " + iBeacon.getProximity()
+ "ProximityUuid: "
+ iBeacon.getProximityUuid() + "Rssi: "
+ iBeacon.getRssi() + "TxPower: "
+ iBeacon.getTxPower();
toastToDisplay(line);
}
In case you haven't already seen it, this Android iBeacon Library sample application is a good reference.