CalendarView Issues when Used Directly (Outside of a DatePicker) - android

I'm using a CalendarView directly, not a DatePicker, b/c its being used as a drop-down/pop-up style dialog where space is factor ( the user clicks a button located to the right of date field, and the CalendarView drops down, appearing directly below the field, aligned/anchored to the button).
There are two show-stopping issues I've ran into, and spent an entire 10 hour day debugging with no resolve.
Issue #1 - Weeks Missing
Weeks appear to missing, and depending on the date, sometimes the week missing will correspond to the "default"/current date (the date the displayed record contains that was loaded from the DB), and so when I display the CalendarView, and call setDate() to auto-select it, no date will appear to be selected (although the CalendarView will be centered around the missing week).
The way I've been fixing this is to manually scroll down, and then back up a few months, and the refresh usually fixes the display. However, I haven't found a way to automate this scrolling, which could be a potential work around. I tried calling setDate() in succession to do this, but it seems to only work on the first call, which brings me to my next issue.
Issue #2 - setDate() Auto-Scrolling Not Working
It seems that only the first call to setDate() will cause the CalendarView to be centered around the corresponding date. If I choose a new date (and store it in private member) and dismiss the popup, and then bring it back up with another dropdown-button click, which will now call setDate() with this new date, then the CalendarView will be centered around the old/previous date, even though the new date is actually hilited (which can be confirmed my manually scrolling down to it).
I can attach code if required, but before spending the time to do so, I just wanted to see if this is a well known issue.
Thank you.

This is just a work-around (also, I've been working in Monodroid/C#, not raw Android)
By first calling SetDate() with a value for the last day of the month previous to the desired display date, and then calling it with the desired date, it seems to work consistently as expected (make sure this date falls between the Max/Min dates of the CalendarView).
However, the second call needs to be posted as a runnable to the UI thread.
Here is some code (note the boolean flags to SetDate, for centering and scroll-animation, neither of which work, hence this work-around):
View view = _inflater.Inflate( Resource.Layout.CalendarViewPicker, null );
CalendarView cvPicker = view.FindViewById<Android.Widget.CalendarView>( Resource.Id.cv_picker );
DateTime lastDay = new DateTime(_date.Year, _date.Month, 1).AddDays(-1);
long lastDayTicks = Ticks( lastDay.Date );
cvPicker.SetDate( lastDayTicks, true, true );
cvPicker.Post( () => {cvPicker.SetDate(Ticks(_date.Date),true,true)} );
Passing _date.Date to Ticks() passes a DateTime with a Time of 12:00am (basically "zeros out" time element), as opposed to passing just _date, which is also a DateTime. Also, a conversion function is needed, since I'm using C#/.NET, to convert the tick-offset
private long Ticks( DateTime date )
{
DateTime _1970 = (new DateTime(1970, 1, 1)).AddDays(-1);
return (date.Ticks - _1970.Ticks) / 10000;
}
Resource.Layout.CalendarViewPicker.axml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="#dimen/calendar_view_width"
android:layout_height="#dimen/calendar_view_height"
android:background="#FF666666">
<Button
android:id="#+id/btn_picker_exit"
android:layout_width="60dp"
android:layout_height="36dp"
android:layout_alignParentTop="true"
android:background="#drawable/cv_button_background"
android:gravity="center"
android:textColor="#FFFFFFFF"
android:textSize="20sp"
android:text="+"/>
<CalendarView
android:id="#+id/cv_picker"
android:layout_width="#dimen/calendar_view_width"
android:layout_height="#dimen/calendar_view_height"
android:shownWeekCount="6"/>
<RelativeLayout/>
My reasoning for the first call to SetDate() with the last day of the previous month, was b/c many times the calendar would popup with the second week of a given month showing as the first visible week, and when scrolling up to see the first week, I would find it missing. So, I figured setting the date with the last day of the previous month might force the first week (ie, the problematic week) to show.
Now with the first week showing, I call SetDate again, but it only works when posted, b/c when posting a runnable to the UI, the event is scheduled to occur after the associated view is rendered. If 2 SetDate()'s are called consecutively (neither in a runnable), say in OnCreate(), then the effects take place before the view is rendered, and so only the second call would take effect, overwriting the first (and therefore loosing the effect of forcing the first week to show).
These two calls also fix the centering/auto-scrolling issue, which is nice too.

Related

Android Life Cycle confusion

Updated to describe the requested code; the updates are at the end.
I'm trying to do something that seems like it should be simple but the Android Life Cycle doesn't seem to be working as described. I expect this is a misunderstanding on my part, not a bug in Android!
I am new to Android and am just getting familiar with the Android Life Cycle after developing Java apps for Windows so I'm still struggling with a lot of new ideas, especially the Life Cycle.
I have nearly finished my first app, which is based on a RecyclerView. It shows a bunch of sales records (each representing one sale) of a small company. Users can click on a FloatingActionButton to report a new sale or click on edit or remove icons on the individual records to change the details of a sale or delete it entirely. Each of these things has its own activity. Also, there is a SettingsActivity to show settings.
One of the settings lets the user select from amongst three different date formats for displaying the date of the sale. When I back out of Settings (by hitting the back button), I want to see the visible rows using the newly-changed date format immediately but this is NOT happening. Up until now, I would just click on the buttons to take me to one of the other activities, then click the cancel button when I get there; on returning to my main activity, I would see the new date format.
I don't feel my user should have to go to that other activity; simply returning from Settings should change the dates immediately. I started looking at the Android Life Cycle. As I read the documentation, I should be overriding the onResume() method and issuing a notifyDataChanged() to the Adapter that controls the RecyclerView to get it to rebind the visible rows, using the new date format.
Unfortunately, that has no effect whatever that I can see. Here's my onResume:
#Override
protected void onResume() {
super.onResume();
mAdapter.notifyDataSetChanged();
}
Am I doing the notifyDataSetChanged() in the wrong method? If not, why doesn't it work? If it is the wrong method, which method should I be overriding?
Is there any particularly good text or video tutorial explaining the Life Cycle that I should be reading or viewing?
Updates
The date format is set in my SettingsActivity, which is the default one provided in the SettingsActivity template, tailored to meet my needs. All I've touched is the General settings. I replaced all of the ones from the template with six of my own settings. One is called Date format and defaults to YYYY-MM-DD; there are two other choices.
My main activity, which is the one that invokes SettingsActivity via a menu option, gets the value of the date format from the default SharedPreferences. This is because I don't want to look up the value of the Date format once for every row in the ArrayList but just once for all the rows in the ArrayList. I pass the value of the Date format to the Adapter via its constructor. The format gets used in OnBindViewHolder() to control the appearance of the date on the various sales records.
Define a set method to your adapter,
public class MyAdapter extends RecyclerView.Adapter<AdapterContactList.ViewHolder> {
private String dateFormat;
.....
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
}
After user change the dateformat; set it to your adapter, then notify datas for change. That means, you need set new dateformat before notifyDataSetChanged method; because adapter doesn't know the new value.
#Override
protected void onResume() {
super.onResume();
String newDateFormat = getFromSharedPreferencesOrInstantly();
mAdapter.setDateFormat(newDateFormat);
mAdapter.notifyDataSetChanged();
}

Android UIautomator swiping ViewAnimator

I'm testing my app but have a problem with the DatePicker.
All I need is to swipe down until another month appears (searching for a specific date).
The structure is a bit tricky but I made it work using
new UiScrollable(new UiSelector().className(android.widget.ViewAnimator.class.getName())).scrollTextIntoView("November")
Problem is, even though the view is scrolling, it is
1) scrolling in the wrong direction (up)
2) not stopping even though a November has already passed.(even November 2017 or November 2015...)
How can I create this condition. After all the views are named in a unique way so checking the structure would be possible finding "01 November 2016".
Okay, looks like I found a solution that works more or less fine:
At first I tried to use the sub conditions since UiSelector might have a child definition. But that proved hard since inside of the ViewAnimator is a (single) child object ListView containing the "calendar month" view.
So finding a childview in there with the description "15 December..." was tricky.
The new solution does this.
while (tries < MAX_TRIES)
if element with description "15 December ..." exists
-> click and exit loop
scroll down 1 element (next month)
-> tries++
I repeat this until the element is found (and clicked) or the maximum scroll tries are exhausted.
After scrolling I let the device wait for 1 second. This is useful since swiping is called asynchronously and would continue swiping for a moment while processing the click.
This did not show as a problem (since the view is already clicked) but still might be confusing.
I hope this helps! Feel free to post a better solution if you find one.
int tries = 0;
while (tries < MAX_TRIES) {
UiObject2 dateField = mDevice.findObject(
By.descStartsWith(SEARCH_DATE));
if (dateField != null) {
dateField.click();
break;
} else {
tries ++;
new UiScrollable(new UiSelector().
className(android.widget.ViewAnimator.class.getName())).
scrollToEnd(1);
mDevice.wait(Until.findObject(By.descStartsWith(SEARCH_DATE)), 1000);
}
}

Android setBackground(Drawable) consumes too much time

I have a tablelayout for a calendar with 300 cells. When the user touches dates in a flow/move touch e.x. 6 days, it sets a background drawable.
The drawable is set with XML (shape, rect, ...). It iterates only over this 6 days, not over all cells.
The Problem is, that on Nexus 7 and older devices setting a backgroundDrawable consumes too much time. When the finger is on the 6th day, it sets the 4th day till it also arrives the last day!!!
How can I improve it ?
Flow: onDown(...), onMove(...) over 6 days, onUp(...).
EDIT:
public boolean onMove(MotionEvent e) {
Day selectedDay = searchForSelectedDay((int) e.getX(), (int) (e.getY() + getScrollY()));
if (selectedDay != null && selectedDay != mPreviousDay){
defineDayContent(mPreviousDay, selectedDay);
}
...
defineDayContent(Day arg0, Day arg1){
//iterates from arg0 till arg1 and sets the background
for(Day d : (iterate from arg0 till arg1)){
d.setBackground(Calendar.BACKGROUND_DRAWABLE);
}
}
You provided no code, that's why it's difficult to say where exactly the bottleneck is. I would try following measures.
As the first measure I would preload drawables making sure I don't call getDrawable() or setBackgroundResource() inside onTouch() method.
Secondly, I will make sure I don't call setBackground() in onTouch() for a view twice or multiple time, if background has already been set.
If first two don't help, I would call setBackground() delayed, only after a user slows his/her finger down over a certain view. This will avoid visual effect you described and speed up updates.
This was not solved, but ...
We tried to inflate views and set visibility of imageviews. Very quick, but the inflating of xmls for 300 cells was to heavy... 2-3 seconds to init.
We just decided to draw the content of the cells. That is very fast in layouting + in performance.
Solution at the end: CustomView + onDraw
yayyy

Hours, Minutes selector widget

I am porting one of my iOS apps over to android, and one of the layouts uses the UIDatePicker with the mode UIDatePickerModeCountDownTimer. I have searched and searched and searched for something even close in android, and the only thing I have come across is the TimePicker widget which only seems to do time of day.
Does something like UIDatePickerModeCountDownTimer exist in android, or do I have to fenagle my own solution for entering this type of time.
There is no one stop shop that includes a date picker and a time picker, both are seperate
so basically you need a date field that shows a DatePickerDialog and a time field that show a TimePickerDialog when clicked
As far as I am aware you will either need a 3rd party library or you will need to create a custom version of the TimePicker.
EDIT :
Basically i've done something similar like your requirement, i have a time picker dialog in dialog fragment, without am/pm indicator. You only have to set the DateFormat to 24 hour format in onCreateDialog method. Something similar like this :
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current time as the default values for the picker
int hour = 0;
int minute = 0;
// Create a new instance of TimePickerDialog without am/pm and return it
return new TimePickerDialog(getActivity(), this, hour, minute,
DateFormat.is24HourFormat(getActivity()));
}
I hope this answer help you :)

alternative to the missing setOnDateChangeListener()-function

I have a DatePicker-view as well as a TimePicker-view in my layout, and everytime the date or time is changed I want to update a value. On the TimePicker I can simply set a setOnTimeChangedListener, but nothing corresponding exists for the DatePicker. A listener called OnDateChangedListener can be set in the init()-function, but it does not trigger each time the date is changed. Is there a corresponding listener, or is there a hack available to solve this problem in a satisfactory manner?
http://developer.android.com/intl/de/reference/android/widget/DatePicker.html
http://developer.android.com/intl/de/reference/android/widget/TimePicker.html
Cheers,
A listener called
OnDateChangedListener can be set in
the init()-function, but it does not
trigger each time the date is changed.
According to the source code, it is called each time the date is changed. So, try that again.

Categories

Resources