Okay so I've been able to customize a few SeekBar's to be used as a Bar Graph type of image, but since I need to be able to switch between a Green or Red Image (depending on certain values) which you can see below. The problem is that regardless of what value I use in the setLevel for the Drawable it doesn't fill appropriately (you can see the image below for an example since the green bar should be closer to the right based on the two values)
Below is the code for the section that setups this entire MTD Commission bar section, I don't know how much of the code you would need to see so I just decided to post all of this section.
void setupMTDBarSection() {
//Get Current Date and Number of Days in Current Month
Calendar cal = Calendar.getInstance();
int numberOfDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
SimpleDateFormat today = new SimpleDateFormat("dd");
String currentDate = today.format(new Date());
//Get MTD Goal value from Preferences
String goalString = preferences.getString("keyMonthlyGoal", "0");
float mtdGoalFloat = Float.valueOf(goalString);
Integer mtdGoal = (int)mtdGoalFloat;
MTDGoalValue.setText(NumberFormat.getCurrencyInstance().format(mtdGoalFloat));
//Get Current MTD Value
String mtdString = preferences.getString("keyMTDValue", "0");
float mtdValueFloat = Float.valueOf(mtdString);
Integer mtdValue = (int)mtdValueFloat;
MTDCurrentProgress.setText(NumberFormat.getCurrencyInstance().format(mtdValueFloat));
//Do some math to determine if the Rep is below/above the daily goal
Integer dailyGoal = mtdGoal/numberOfDays;
Integer currentDayGoal = dailyGoal * Integer.valueOf(currentDate);
if (mtdValue >= currentDayGoal) {
MTDGreenTrack.setLevel(mtdValue);
MTDProgressBar.setProgressDrawable(MTDGreenTrack);
MTDProgressBar.setMax(mtdGoal);
MTDProgressBar.setProgress(mtdValue);
}
else {
MTDRedTrack.setLevel(mtdValue);
MTDProgressBar.setProgressDrawable(MTDRedTrack);
MTDProgressBar.setMax(mtdGoal);
MTDProgressBar.setProgress(mtdValue);
}
//Add Percentage to MTD Text
NumberFormat percentFormat = NumberFormat.getPercentInstance();
float percent = mtdValueFloat/mtdGoalFloat;
String percentage = percentFormat.format(percent);
MTDPercentText.setText("(" + percentage + ")");
//Setup MTD Indicator
MTDIndicator.setMax(numberOfDays);
MTDIndicator.setProgress(Integer.valueOf(currentDate));
MTDIndicator.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return true;
}
});
}
I believe I found the issue. Apparently when you call setProgressDrawable more than once then you need to re-draw the image that is used since it looses the format that was previously there. Also, don't really need to set the level each time of the drawable as well, it matches up with the progress value of the Seekbar. Below is the code that works for me so far
Rect bounds = MTDProgressBar.getProgressDrawable().getBounds();
MTDProgressBar.setProgressDrawable(MTDGreenTrack);
MTDProgressBar.getProgressDrawable().setBounds(bounds);
MTDProgressBar.setProgress(mtdValue);
MTDProgressBar.setMax(mtdGoal);
Related
I have tried figuring this out, but it doesn't add up. The data doesn't appear as it should.
First I generate dummy data. This is done async because I need time between the calls to System.currentTimeMillis to get some spacing between them. (Look aside the crappy code here, this is just debug data that will not be in the release. Using Thread.sleep on the main thread is a bad idea considering ANR's)
public class AsyncGeneration extends AsyncTask<String, String, String>{
public AsyncGeneration() {
super();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onCancelled(String s) {
super.onCancelled(s);
}
#Override
protected void onCancelled() {
super.onCancelled();
}
#Override
protected String doInBackground(String... strings) {
while(root == null) {
try {
Thread.sleep(200);
}catch(InterruptedException e){}
}
List<Entry> rdata = new ArrayList<>();
for(int i = 1; i < 30; i++){
try{
Thread.sleep(200);
}catch(Exception e){
//IGNORE
}
float time = System.currentTimeMillis();
Log.e("GChart", "Timex: " + time);
Session s = new Session(r.nextInt(5000), time);//Replace time with the index in the for-loop and it works for some reason
rdata.add(new Entry(s.getXValue(), s.getYValue()));
Log.e("GChart", "Timey: " + s.getXValue());
}
final List<Entry> entries = rdata;
OverviewFragment.this.getActivity().runOnUiThread(() ->{
LineDataSet data = new LineDataSet(entries, "Distance");
data.setCircleColor(Color.parseColor("#FF0000"));
LineData lineData = new LineData(data);
tab1chart.setData(lineData);
tab1chart.invalidate(); // refresh
tab1chart.getXAxis().setValueFormatter(new DateFormatter());
tab1chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
tab1chart.getXAxis().setTextSize(10f);
tab1chart.getXAxis().setTextColor(Color.RED);
tab1chart.getXAxis().setDrawAxisLine(true);
tab1chart.getXAxis().setDrawGridLines(true);
tab1chart.getAxisLeft().setValueFormatter(new DistanceValueFormatter());
tab1chart.getAxisLeft().setDrawGridLines(true);
Log.v("Chart", "Chart data loaded and invalidated");//This prints
});
return null;
}
}
So far everything looks fine. The data gets put into the chart, no exceptions, no crashes.
When the chart renders, a single data point shows up at the far-left of the chart. I generate 30 data points, one shows up.
That's issue #1: Only one data point shows up.
Issue #2 is slightly harder. The entire X axis at the bottom disappears. X axis is gone and when zooming, the Y axis, and its text also disappears. This is fairly hard to explain, so here is a screenshot:
It is worth mentioning the fact that if I pass i in the for-loop as the time, it shows up just as expected: all the axises are in place, zoom doesn't break anything.
(Float values can take the same values as Longs except they have decimals in addition.)
And in addition, I format the data:
public class DateFormatter implements IAxisValueFormatter {
SimpleDateFormat formatter;
public DateFormatter(){
formatter = new SimpleDateFormat("dd/MM/yy, HH:mm");
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
//This method is never called. Nothing is returned
if(axis instanceof XAxis) {
String formatted = formatter.format(new Date((long) value));
Log.d("Formatter", "Formatted \"" + value + "\" to \"" + formatted + "\".");
return formatted;
}
return "Not supported";
}
}
Removing the formatter doesn't change anything, the axis is still gone. Zoom still breaks the Y axis.
So my question is: How do I fix this? I don't get why the chart doesn't work when I pass the current time in milliseconds (and I checked the values with debug output, floats can handle it). I got some debug output earlier that eventually stopped coming at all that showed the value passed to the formatter was values < 2000. I can't reproduce this any more though
The chart itself doesn't appear to be broken but from touch events it looks like every single point is pushed into the same X coordinate but only one point renders. When I touch the chart, the orange-ish lines show up indicating the position of a data point. It pops up on points that aren't visible.
When I pass the for-loop index as the X value, it works as expected, the formatter works fine (looking aside the fact that it shows the date as in 1970, but it is counted as 1 millisecond into the epoch, so that is to expect)
I looked at this as well on formatting the date. When I then try passing the milliseconds since the epoch, the chart stops working.
I'm using MPChart v 3.0.2, compiling against Android 26, using Java 8, and running Android Studio 3.0 beta 2
As for the layout, it is just a LinearLayout with a LineChart in it.
This should work, but it breaks when I pass it the current time in milliseconds for some reason. Passing any other data (as long as the numbers aren't that big) works fine.
The two images are not how the chart is supposed to look. The X axis is supposed to be visible, but for some reason it isn't along with a large amount of the data.
This is known issue of Android MPChart (have a read this thread). Time series chart supported in MPChart - You can set time (in millis) as X values for Hourly Charts.
But too many consecutive data (points) won't correctly plot in Line Charts. Because Entry object will accept only float values due to some performance constraints.
So, keep the first value as the reference and subtract each up coming value from reference value & divide by some constants (say 1000) .So , you X value set will be like 10,20,30....& so on.
Do the reverse logic in your Axis Value formatter to render the X Axis Label properly (see the code snippet).
lineChart.getXAxis().setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
SimpleDateFormat format2 = new SimpleDateFormat("HH:mm:ss");
return format2.format(new Date(firstTimeStamp + ((long) value) * 1000L));
}
});
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.
I was wondering if it is possible to run a check against all the TextViews in an Activity/View and make ones which don't have any content in them invisible? I don't really want to wrap a if statement around each of the TextViews when setting text.
I currently have an activity that will populate a range of TextViews with content from a Database. However there is a chance that some fields in the database will not have any content and to keep a clean UI I want to simply make these fields invisible instead of them just sitting blank.
Here is where I am setting the content:
public void settingContent() {
eventTitle.setText(event.EventTitle);
eventLocation.setText(event.SiteName);
organiser.setText(event.Organiser);
eventType.setText(event.setEventTitle());
DateFormatter dateFormatter = new DateFormatter();
String startDate = dateFormatter.getFormattedDate(event.StartDateTime);
String endDate = dateFormatter.getFormattedDate(event.CloseDateTime);
String eventDates = startDate + " - " + endDate;
dates.setText(eventDates);
cost.setText(event.FeeDetails);
bookingContact.setText(event.BookingContactName);
stewardContact.setText(event.StewardContactName);
unitTypes.setText(event.MaxUnits);
mapReference.setText(event.MapReference);
eventNumber.setText(event.EventNumber);
otherInformation.setText(event.OtherInformation);
directions.setText(event.SiteRouting);
if(event.isTempHoliday()) {
eventIcon.setImageResource(R.drawable.icon_holidaysites_card);
} else {
eventIcon.setImageResource(R.drawable.icon_clubmeets_card);
}
}
So is this possible or is it a case of just having to check each individual TextView before setting?
List<TextView> lstTexts = new ArrayList<TextView>();
lstTexts.add(cost);
lstTexts.add(bookingContact);
lstTexts.add(stewardContact);
lstTexts.add(unitTypes);
lstTexts.add(mapReference);
lstTexts.add(eventNumber);
lstTexts.add(otherInformation);
lstTexts.add(directions);
//then you can run this method
boolean empty = checkAllTV(lstTexts);
This is the method for it.
public boolean checkAllTV(List<TextView> allTV){
for(TextView tv : allTV){
if(tv.getText().toString().equals("")||tv.getText().toString()==null)
return false;
}
return true;
}
I am on Mobile so I am not able to check the code! but you can get the idea.
Hope it Helps.
HI everyone,
I have a bunch of text in an Edit text that I have set up to be styled(strike through only for the moment) using the setSpan method in Android. This seems to work fine.
The trouble I am having is that all the styling seem to get cancelled once I close that activity. That is when I load up the activity again , it just has plain text and none of the styling I had applied using the setSpan().
Note: All of my text get stored in a Database.
I have attached all the code for the styling, let me know if you need to see any more bits of code.
private void doStrick() {
int selectionStart = mBodyText.getSelectionStart();
styleStart = selectionStart;
int selectionEnd = mBodyText.getSelectionEnd();
// check for boo-boo's
if (selectionStart > selectionEnd){
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
Spannable str = mBodyText.getText();
str.setSpan(new StrikethroughSpan(),selectionStart, selectionEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
Is that some bit of code I need to be adding to save the styling?
EDIT based on answer:
Calendar cal=Calendar.getInstance();
String date_time=String.format("%1$te %1$tB %1$tY,%1$tI:%1$tM:%1$tS %1$Tp",cal);
float Textsize = mBodyText.getTextSize();
String title = mTitleText.getText().toString();
String body = Html.toHtml(mBodyText.getText());
if (mRowId == null) {
long id = mDbHelper.createNote(title, body, date_time, Textsize);
if (id > 0) {
mRowId = id;
}
} else {
mDbHelper.updateNote(mRowId, title, body, date_time, Textsize);
Log.d("MYTAG", "updateing note");
updateWidget();
And where I populate the fields:
Cursor note = mDbHelper.fetchNote(mRowId);
startManagingCursor(note);
mTitleText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
mBodyText.setText(Html.fromHtml(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY))));
mBodyText = (EditText) findViewById(R.id.body);
float size = note.getFloat(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TEXT_SIZE));
mBodyText.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
Is that some bit of code I need to be adding to save the styling?
Yes.
Presumably, right now, you are just saving thisIsYourEditText.getText().toString() to your database, then using thisIsYourEditText.setText(stringThatYouLoadBackOutOfYourDatabase) to populate the EditText.
Instead, you need to use Html.toHtml(thisIsYourEditText.getText()) to try to convert your styled text into HTML, and then use thisIsYourEditText.setText(Html.fromHtml(stringThatYouLoadBackOutOfYourDatabase)) to convert that HTML back into styled text.
Note that toHtml() and fromHtml() do not handle all possible CharacterStyles, nor are they guaranteed to do all of the styling correctly round-trip (i.e., the string generated by toHtml() may not match the string you started with before the fromHtml() call).
I am trying to figure out how to change the color of TextView based on the value of the text.
TextView has been sent from another activity I have that part working fine. What I want is a way to change the color of the text based on what is in the TextView. So if previous Activity sends a value like "11 Mbps" as TextView then I would like that text color to be yellow, "38 Mbps" green, and 1 Mbps red. I'm using eclipse if that helps at all.
This is how I'm sending the TextView to another activity. "showmsg" is just username sent to another page.
buttonBack.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){
final TextView username =(TextView)findViewById(R.id.showmsg);
String uname = username.getText().toString();
final TextView wifistrength =(TextView)findViewById(R.id.Speed);
String data = wifistrength.getText().toString();
startActivity(new Intent(CheckWiFiActivity.this,DashboardActivity.class).putExtra("wifi",(CharSequence)data).putExtra("usr",(CharSequence)uname));
}
});
And this is how I receive it in the other activity
Intent i = getIntent();
if (i.getCharSequenceExtra("wifi") != null) {
final TextView setmsg2 = (TextView)findViewById(R.id.Speed);
setmsg2.setText(in.getCharSequenceExtra("wifi"));
}
This all works fine but I don't have a clue how to change the color of TextView based of the value of the text. Any help would be really appreciated.
You obviously want to set the color according to the number in the String you received from the previous Activity. So you need to parse it out of the String, save it to an int and then according to what the number is, set the color of your TextView.
String s = in.getCharSequenceExtra("wifi");
// the next line parses the number out of the string
int speed = Integer.parseInt(s.replaceAll("[\\D]", ""));
setmsg2.setText(s);
// set the thresholds to your liking
if (speed <= 1) {
setmsg2.setTextColor(Color.RED);
} else if (speed <= 11) {
setmsg2.setTextColor(Color.YELLOW);
else {
setmsg2.setTextColor(Color.GREEN);
}
Please notice that this is an untested code, it might contain some mistakes.
The way to parse it comes from here.
First, get all of the non-numeric characters out of your String and convert it to an integer. Then use a switch on the new value and set the color accordingly
String color = "blue"; // this could be null or any other value but I don't like initializing to null if I don't have to
int speed = i.getCharSequenceExtra("wifi").replaceAll("[^0-9]", ""); // remove all non-digits here
switch (speed)
{
case (11):
color = "yellow";
break;
case (38):
color = "green";
break;
case(1):
color = "red";
break;
}
setmsg2.setTextColor(Color.parseColor(color);
Here is a little site with some handy information
Color Docs