ScrollView with too many Textviews (over 400) - android

I was able to make an app with a scrollable calendar, like the image:
Preview and Component Tree
The calendar itself, for 2016, looks like this:
2016 Calendar
It's a five week based calendar, and it's meant to be built like that, for its purposes.
When you run the app you have a calendar, with a week or so displayed.Then you can scroll the days and see the whole calendar. The months names stand still on the left.
Clicking the arrows updates the calendar, showing past and future years, as expected.
It works.
I still don't understand A LOT of java, but I could manage to make it work.
One search here, another there, one tutorial here, another example of code there...
It works.
The problem is: it's too slow to update the calendar.
It takes about 3 seconds or so to update the days, and you can't scroll the calendar during that time.
After the update the scroll is normal. You go right and left. No problem.
It's the processes to update all those TextViews that's killing me... Yes, I do have A LOT of TextViews, and that's where I think I could start getting some help.
The calendar is designed to display five weeks, monday to sunday, which gives me 35 TextViews per month... that's 420 TextViews...
The following method clears the calendar prior to update:
public void Clears_the_Calendar() {
int iId;
TextView tvDay;
for (iId = 0; iId < 420; iId++) {
tvDay= (TextView)findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText(" ");
}
}
As you can see, I use the array iarrTextView_Days_ids to reference the TextViews. It looks like:
int[] iarrTextView_Days_ids = new int[]
{R.id.M01_D01, R.id.M01_D02, ... , R.id.M01_D34, R.id.M01_D35,
R.id.M02_D01, R.id.M02_D02, ... , R.id.M02_D34, R.id.M02_D35,
.
.
.
R.id.M12_D01, R.id.M12_D02, ... , R.id.M12_D34, R.id.M12_D35}
And this is what I use to build the calendar:
private void mtBuild_Calendar(int iYear, int iColumn) {
int i, iId, iDay, iMonth1, iMonth2, iTotal_Days;
TextView tvDay;
Date dtDate = null;
int iRight_Column = 35;
String sDay, sDate = "01/01/" + iYear;
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Calendar cMain_Calendar = Calendar.getInstance();
Calendar cAuxiliary_Calendar = Calendar.getInstance();
try {
dtDate = sdf.parse(sDate);
cMain_Calendar.setTime(dtDate);
cAuxiliary_Calendar.setTime(dtDate);
} catch (ParseException e) {
e.printStackTrace();
}
cMain_Calendar.setTime(dtDate);
cAuxiliary_Calendar.setTime(dtDate);
Clears_the_Calendar();
// displays "1" (jan/1), according to iColumn, which is determined in another method:
iId = iColumn - 1;
tvDay = (TextView)findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText("1");
// It's not important to be extremely precise about leap years:
if ((iYear % 4) == 0) {
iTotal_Days = 366;
} else {
iTotal_Days = 365
}
for (i = 1; i < iTotal_Days - 1; i++) {
// Adds a day to the cAuxiliary_Calendar and compares its MONTH with the previous day from cMain_Calendar
cAuxiliary_Calendar.add(Calendar.DATE, 1);
iMonth1 = cMain_Calendar.get(Calendar.MONTH);
iMonth2 = cAuxiliary_Calendar.get(Calendar.MONTH);
// Performs the comparisons needed to change the months:
if (iColumn < iRight_Column) {
if (iMonth2 == iMonth1) {
iColumn = iColumn + 1;
iId = iId + 1;
} else {
iColumn = iColumn + 1;
iId = iId + 36;
}
} else {
if (iMonth2 == iMonth1) {
iColumn = 1;
iId = iId - 34;
} else {
iColumn = 1;
iId = iId + 1;
}
}
// Adds a day to cMain_Calendar and displays it in the proper place according to iId:
cMain_Calendar.add(Calendar.DATE, 1);
iDay = cMain_Calendar.get(Calendar.DAY_OF_MONTH);
sDay = Integer.toString(iDay);
tvDay = (TextView) findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText(sDay);
}
}
At first a method clears all the 420 TextViews, and then just the 365 or 366 needed for the year are fulfilled.
Still, that's a lot of work... this afternoon I started taking a look at canvas.drawText
For a moment it seemed to me that it would be faster, cause for what I understood TextView uses canvas inside its core. But I couldn't even find a way to make a simple canvas.drawText to work inside the ScrollView.
So the questions are:
Should I use canvas.drawText instead of TextViews? How can I do that?
Or, is there any other faster way to display 365 numbers like the app is supposed to do?
(new info, not sure how it looks...)
Trying new thigs:
Perhaps I misunderstood what adapters can do, but for what I've learned it won't help me... cause once an year is fully loaded into the view there's no delay in scrolling the calendar. The problem is how long it takes to refresh all the 365 TextViews when I want to change from one year to another. In fact, I could manage to reduce that delay to less than 1 second at this point, cause I finally follow the Android Studio advice about too many nested weights.. All my TextViews where using that attibute on its style. I removed them all, letting weights just for the LinearLayouts.
Right now I'm stuck in trying to find a way to pass a string to a method which could draw a text into the view. I still can't get a drawText to work inside the ScrollView, cause I can't find a way to call it. Seems to me that it would only works inside an onDraw, which only triggers when I load a view. But I need to get drawText doing its job 420 times (when I clear the calendar) plus 365 times when a new calendar is shown.
Am I supposed to load 800 views into the ScrollView, each time I want to chage the year ???
I also changed the main LinearLayout to RelativeLAyout, but things are getting weird cause I had to set a lot of heights (which was 0dp prior to the change) and now I'm having troubles on changing from portrait to landscape... but that's a minor issue.

Related

android, disabling the button even if user exits the app

My program saves some data to SQLITE with datetime. There is a button save, which saves the value of 1 spinner along with datetime.
I will provide an example to make sure all understand:
User inputs at 20:03:24, now i want to disable the save button until the HOUR is at least 21:00. So every save can only be on every full hour.
Now my idea is to read the hour from SQLITE and check if the last inputed hour is != current Hour then
btn.setClickable(true);
else
btn.setClickable(false);
This is what i have created and partially works.
public void CHECK(){
sqliteDbHelper_ = new Sqlite_DBHelper(getActivity());
sqLiteDatabase = sqliteDbHelper_.getWritableDatabase();
String[] columns = {"hour_only","JAKOST"};
cursor = sqLiteDatabase.query("podatki", columns,null,null,null,null,null);
Calendar calander = Calendar.getInstance();
Integer currentHour = calander.get(Calendar.HOUR_OF_DAY);
gumb_poslji.setClickable(true);
cursor.moveToLast();
Integer lastInputedHour = cursor.getInt(0);
Log.e(TAG+"zadnja vnesena URA",""+lastInputedHour);
Log.e(TAG+"trenutna URA",""+currentHour);
if (lastInputedHour == currentHour){
gumb_poslji.setBackgroundColor(getResources().getColor(R.color.TextMainColor));
gumb_poslji.setClickable(false);
}
else
gumb_poslji.setClickable(true);
gumb_poslji.setBackgroundColor(getResources().getColor(R.color.colorGreen));
}
How to implement a listener on the app that will constantly looking if there is a difference in the app?
Also be aware the button must also change state from clicable to not clicable even if a user is in the app and the hour changes.
Since i am new to java and Android i am looking for an idea or an example how to do this, not a solution. Am i thinking in a right direction?

Different behavior of PeriodFormatter on different devices (JodaTime)

I'm implementing count down timer for the android app using JodaTime.
Depending of devices the output is different.
DateTime openingDateTime = new DateTime(2018, DateTimeConstants.JUNE, 14, 21, 0, 0, DateTimeZone.forID("Europe/Moscow"));
DateTime nowDateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"));
long difference = openingDateTime.getMillis() - nowDateTime.getMillis();
(...)
onTick(difference);
(...)
PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
.printZeroAlways()
.appendDays().appendSuffix(" day", " days")
.appendSeparator(" ")
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
(...)
#Override
public void onTick(long millisUntilFinished) {
Duration duration = new Duration(millisUntilFinished);
Period period = duration.toPeriod(PeriodType.dayTime());
tvCounter.setText(periodFormatter.print(period));
}
On the one device output is correct: 491 days 4:39:18
on the other is wrong: 0 days 11788:49:11.
What am I doing wrong?
Thanks to your comments, I can now reproduce your problem. Just add following static initializer to your test class (at first place) to simulate the device where you observe your expected output:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
According to the spec (see also the accepted answer on this SO-post), the conversion duration.toPeriod(periodType) should only use so-called precise duration fields, that is hours, minutes, seconds and milliseconds but not days.
My analysis of source code of Joda-Time (v2.9.6):
The internal class org.joda.time.chrono.BasicChronology contains following constant:
private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);
So we see that here this duration field is marked as "precise", but: The subclass ZonedChronology wraps it and override the behaviour of method isPrecise():
public boolean isPrecise() {
return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
}
This shows an extra zone dependency of the precision property of the days()-duration-field, namely precise for fixed zones like UTC and else imprecise.
I don't know if the analyzed and observed behaviour is a feature or a bug. Let's say, it is dangerous to expect the creation of Period-objects by duration.toPeriod(...) to be zone-independent. And it is not documented there to have a precise days-component if the system zone is fixed.
Unfortunately, the implicit dependency on the default time zone is deeply coded into Joda-Time via its chronology-design. As workaround, you can use:
Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());

why my code is so slow?

i runing this code on android after load a cursor with the query i pass to the adapter, but my date is in long in milliseconds format so i need to format properly before load the adapter!
problem is this code is taking 14 seconds to pass a 50 items load, the problem get worst if i call it inside the adapter getView cause get slow when i scrool, if i take this function out the program runs smoothly
this is call inside my listfragment
private String dateFormatPatternEdited(long timeMS) {
android.text.format.DateFormat df = new android.text.format.DateFormat();
final Calendar eDate = Calendar.getInstance();
Calendar sDate = Calendar.getInstance();
sDate.setTimeInMillis(timeMS);
long daysBetween = 0;
while (sDate.before(eDate)) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
String mDateFormatPattern = FuelTrackerApplication.dateFormat.format(timeMS);
if (daysBetween < 2){
mDateFormatPattern = FuelTrackerApplication.timeFormat.format(timeMS);
} else if(daysBetween < 365){
mDateFormatPattern = df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
}
return mDateFormatPattern;
}
and this is were i initialize the date formats i gonna use its called inside onCreate in FuelTrackerApplication i dont think theres nothing wrong with this
public void initializeDateFormat() {
android.text.format.DateFormat df = new android.text.format.DateFormat();
dateFormatPattern = "MMM dd";
if (android.os.Build.VERSION.SDK_INT >= 18){
dateFormatPattern = df.getBestDateTimePattern(Locale.getDefault(), dateFormatPattern);
}
dateFormat = df.getMediumDateFormat(getApplicationContext());
timeFormat = df.getTimeFormat(getApplicationContext());
dateFormat2 = df.getLongDateFormat(getApplicationContext());
}
Ok just a few things. Depending on how long ago your dates are going back. You are only interested if the days between go more then 365. So if your dates are going back for years, you're doing extra work.
while (sDate.before(eDate) && daysBetween <= 365) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
Will let it break, it means if you have 20 entries going back 5 years, you don't do so much work.
It might be worth while to possibly just check the milliseconds difference. I'm not sure if this is precise enough, but it should work. It means you don't need to loop everything E.g
long millisecondsToday = getMilliseconds;
long timeMs = // you already have this
long millisecondsDifference = millisecondsToday - timeMs;
if (millisecondsDifference < MILLISECONDS_TWO_DAYS) // set a final variable out of this method
// etc
If might also be worth while initialising some of your variables once outside of the method. Like your df, that is being created 50 times, and then just having something set on it. Same with your eDate.
i got this incredible faster and practicaly remove the hole function
instead goes like this
lDateBetween = NOW - timeMS;
if (lDateBetween < DAY)
return FuelTrackerApplication.timeFormat.format(timeMS);
else if (lDateBetween < YEAR)
return df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
else return FuelTrackerApplication.dateFormat.format(timeMS);
all calculation is made using milliseconds i also put 2 final NOW and YEAR, also df and lDateBetween
i think is the fastest i can get!

EditText is gone but values remain in memory

I have a couple of EditTexts arranged on rows and columns.Those EditTexts contain product name,quantity and price and a TextView that shows the total in real time(calculates it each time you write on one of the EditTexts)
I've setup a a button on each row that when clicked sets visibility of the row(3EditTexts for product name,price and quantity) to GONE.
My problem is that after i set the visibility to GONE,though there are no more EditTexts it still calculates their values from before being GONE.
My question now is,what happens when the EditTexts are set to visibility.GONE ?
My app calculates in real time,so when something happens to an EditText,he calculates again..but it's like the values are still there...Isn't this supposed to be the difference between invisible and gone ?
I'll show you the way i calculate(it is called even after you press the X button to erase the EditTexts,not only when you change values inside EditTexts)
public void calculeaza() {
totaltest = 0;
prod = new String[allprod.size()];
pret = new String[allpret.size()];
cant = new String[allcant.size()];
for (int m = 0; m < allprod.size(); m++) {
prod[m] = allprod.get(m).getText().toString();
if (prod[m].matches("")) {
prod[m] = " - ";
}
}
for (int j = 0; j < allcant.size(); j++) {
cant[j] = allcant.get(j).getText().toString();
if (cant[j].matches("")) {
cant[j] = Float.toString(0);
}
}
for (int k = 0; k < allpret.size(); k++) {
pret[k] = allpret.get(k).getText().toString();
if (pret[k].matches("")) {
pret[k] = Float.toString(0);
}
}
for (int l = 0; l < allpret.size(); l++) {
Float temp = Float.parseFloat(cant[l]) * Float.parseFloat(pret[l]);
totaltest = totaltest + temp;
TextView totalf = (TextView) findViewById(R.id.total);
totalf.setText(String.format("Total: %.2f", totaltest));
}
}
Lines Straight from Android dev site..
View.GONE This view is invisible, and it doesn't take any space for layout purposes.
View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
i.e it retains EditText object even after Gone..
You can reinitialise edittext if you dont want it to retain its value...or setText = ""
Above quoted is the only difference...
Hope this helps...
I'm not really seeing in the code you posted anything I can use to answer this question, but there does appear to be some confusion as to what setVisibility does:
INVISIBLE elements are not seen on the page, but they still take up space (there's a hole where they would be)
GONE elements have no visible effect on the screen, from the user's perspective they aren't there. However they are still part of the view.
If you want to remove the object from the view, then you need to call removeView() on its parent.
It may still take up memory after it has been removed from the view, in case your code has kept references to it in any variables.
It may still take up memory after there are no further references to it, at least until the garbage collector gets around to it.
I'm hoping the rather generalized statements above help clarify the situation.

Trying to only do math functions on edittexts users have entered information in on android

I have a 10-field average lap calculator. However, in testing, someone said they normally only run X laps in practice, vs. 10 (let's say 7).
I think I could use an if statement, but there'd be at least 10 of them and a bunch of clumsy code, and I'm not sure on arrays/switch statements exactly. I think all of those might be possible, but my low level of experience has yet to fully comprehend these useful tools.
CURRENT CODE:
double tenLapAvgVar = ((lap1Var + lap2Var + lap3Var + lap4Var + lap5Var + lap6Var + lap7Var + lap8Var + lap9Var + lap10Var) / 10);
So essentially, if someone leaves a field or fields blank, I want to calculate the average based on the populated fields, not 10 (if they leave 3 fields blank, calculate based on 7, for instance). Any help you guys could provide would be much appreciated, thanks!
You could have an ArrayList<EditText> object and a method which iterates over it and adds up the values. Something like:
public double getLapAverage()
{
int noOfCompletedLaps = 0;
double lapAve = 0;
double lapsTotal = 0;
for(EditText text : textBoxes)
{
if(text.getText().toString().length() > 0)
{
//psuedo code, and assuming text is numerical
lapsTotal += Double.parse(text.getText().toString());
noOfCompletedLaps++;
}
}
if( noOfCompletedLaps > 0)
{
lapAve = lapsTotal / noOfCompletedLaps;
}
return lapAve;
}
Maybe it would be better if you used an array instead of 10 different variables.
Then you can use a for statement and initialize them to 0, afterwords let the user fill the array and count how many are not zero.
Finally sum up all the array and divide by the count you previously calculated.

Categories

Resources