How to get period when data is loaded to stop shimmer? - java

I am using Volley for requests and want to add shimmer while data is loading. I can perform it with Handler but I don't know when data will come and can't set exact time.
Here is code in my Fragment. I want to set value false to variable isShimmer and it makes so fast so my shimmer is not running:
String url = "my_url";
JsonArrayRequest getArticleOfTheDayRequest = new JsonArrayRequest(Request.Method.GET, url, null,
response -> {
try {
for(int i = 0; i < response.length(); i++){
JSONObject articleObject = response.getJSONObject(i);
int article_id = articleObject.getInt("article_id");
String title = articleObject.getString("title");
String text = articleObject.getString("text");
ArticleOfTheWeek articleOfTheWeek = new ArticleOfTheWeek(article_id, title, text);
articleOfTheWeekList.add(articleOfTheWeek);
}
articleOfTheWeekAdapter = new ArticleOfTheWeekAdapter(getContext(), articleOfTheWeekList);
recyclerView.setAdapter(articleOfTheWeekAdapter);
articleOfTheWeekAdapter.isShimmer = false;
articleOfTheWeekAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
},
error -> Log.d("Error.Response", error.getMessage() + " ")
);
Here code in my adapter:
public boolean isShimmer = true;
int shimmerNumber = 2;
#Override
public void onBindViewHolder(#NonNull final ArticleOfTheWeekViewHolder holder, int position) {
final ArticleOfTheWeek articleOfTheWeek = articleOfTheWeekList.get(position);
if(isShimmer)
{
holder.shimmerFrameLayout.startShimmer();
}else
{
holder.shimmerFrameLayout.stopShimmer();
holder.shimmerFrameLayout.setShimmer(null);
holder.textViewTitle.setBackground(null);
holder.textViewTitle.setText(articleOfTheWeek.getTitle());
holder.textViewDesc.setBackground(null);
holder.textViewDesc.setText(Html.fromHtml(articleOfTheWeek.getText()));
}
}
As you see if I set isShimmer true, shimmer runs infinite. So I can't get a period when all data is loaded to change value of isShimmer

I've used this library for show shimmer effect while fetching data. First set visibility true to your shimmer layout in your xml file then start shimmer on the start of the activity shimmerViewContainer.startShimmer().
Set shimmerViewContainer.stopShimmer() in your volley method after set data to your adapter in activity file.
As well as stop shimmer on onPause() and onDestroy()

May be it would be easier for you to maintain the shimmer state if you put the shimmer in the parent layout rather than within the adapter layout. I would prefer below approach:
1. Inside the layout of Activity/Fragment
<RelativeLayout>
<RecyclerView/>
<FrameLayout
android:id="#+id/shimmer_container">
<!-- show hide this FrameLayout container depend on state of data loading/loaded -->
<include layout="#layout/list_shimmer"/>
</FrameLayout>
</RelativeLayout>
The list_shimmer may be looks like:
<com.facebook.shimmer.ShimmerFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:shimmer_auto_start="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/just_like_adapter_item_layout" />
<include layout="#layout/just_like_adapter_item_layout" />
<!-- repeat until it's enough to fill the screen -->
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
The important property:
app:shimmer_auto_start="true"
So you don't need to start the shimmer programmatically.
2. Activity/Fragment
void initList(List<Data> items) {
adapter.setItems(items);
shimmer_container.setVisibility(View.GONE);
}

Related

Only one of multiple views fails with findViewById()

I'm trying to reference a TextView from a layout. It works for all other views but for this specific one it returns null. Doing it the same way for all the views, so I can't see why it wouldn't work on this one.
Important to mention is that the layout file showed here is included into b_user_list. All views referenced from within the include works, except for one.
b_user_list.xml:
<include
android:id="#+id/showUserLay"
layout="#layout/c_show_front_layout"
... />
c_show_front_layout.xml:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/volunDbListLay"
... >
<TextView
android:id="#+id/textView932"
... />
<View
android:id="#+id/divider522"
... />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/volunDbRecycler"
... />
<TextView
android:id="#+id/noVolunOrgsFound"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="No organizations found."
app:layout_constraintBottom_toBottomOf="#+id/volunDbRecycler"
app:layout_constraintEnd_toEndOf="#+id/volunDbRecycler"
app:layout_constraintStart_toStartOf="#+id/volunDbRecycler"
app:layout_constraintTop_toTopOf="#+id/volunDbRecycler" />
</androidx.constraintlayout.widget.ConstraintLayout>
ActivityUser.java:
private ConstraintLayout settErrorLay;
private TextView errorTxt;
private TextView errorClose;
private TextView noVolunOrgsFound;
private RecyclerView volunDbRecycler;
private ConstraintLayout volunDbListLay, notVsAccWarning;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.b_user_list);
...
settErrorLay = findViewById(R.id.userIntegrErrorLay);
errorClose = findViewById(R.id.closeUserIntegrErrorTxt);
...
if (errorClose != null && settErrorLay != null) {
// Checks if includeView show_front_layout.xml worked
volunDbRecycler = findViewById(R.id.volunDbRecycler);
volunDbListLay = findViewById(R.id.volunDbListLay);
notVsAccWarning = findViewById(R.id.notVsAccWarning);
noVolunOrgsFound = findViewById(R.id.noVolunOrgsFound);
...
}
...
}
...
I found a way to reference the view, but obviously not the proper way. I ran through all the children of the parent view, and when the id match I add it as the view. Then it didn't return null:
if (noVolunOrgsFound == null) {
for (int i = 0; i < volunDbListLay.getChildCount(); i++) {
if (volunDbListLay.getChildAt(i).getId() == R.id.noVolunOrgsFound) {
noVolunOrgsFound = (TextView) volunDbListLay.getChildAt(i);
}
}
}
So I have ran out of ideas for why this doesn't work. It can't be the <include> since the other sibling views works, the id is the same both in the layout and java, and it works with by finding it within the children of the parent, just not with findViewById().
EDIT: I posted an answer that seemed to be solving the problem, but now I just have the same problem with a RecyclerView. This time, it didn't work to manually loop through the views of the parent and checking when the child view id match the RecyclerView's id..
Is there a limit to how many views can be loaded or referenced in Java or something?
Found a solution, I left out something in the question that I didn't think would have a say in the matter (And I'm still not sure why it does).
Before the findViewById(R.id.noVolunOrgsFound); was a function, and in that function was a button assigned to use the TextView (the one that was null). Not sure how the button makes the TextView null, but apparently it did.
initializeDbSettingsLay();
volunDbRecycler = findViewById(R.id.volunOrgRecycler);
volunDbListLay = findViewById(R.id.volunOrgListLay);
notVsAccWarning = findViewById(R.id.notVsAccWarning);
noVolunOrgsFound = findViewById(R.id.noVolunOrgsFound);
Moving the function after the findViewById(R.id.noVolunOrgsFound);, it now wasn't null anymore. Not sure why the button either made it null or not properly find the view, since it worked on the other layouts that also is used in that button function. Only the TextView became null. I'd like to say that the TextView can't be assigned after the ClickListener has been created, but generally values can be changed later, and so did the other views. So I'm not sure why it failed with the TextView.
Here is the function:
private void initializeDbSettingsLay() {
...
vsSettingsBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
userSettingsBtn.setBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
dbSettingsBtn.setBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
vsSettingsBtn.setBackgroundTintList(ColorStateList.valueOf(ActivityUser.this.getColor(R.color.slight_gray)));
userSettingsLay.setVisibility(View.GONE);
dbSettingsLay.setVisibility(View.GONE);
vsSettingsLay.setVisibility(View.VISIBLE);
learnMoreVsLay.setVisibility(View.GONE);
learnMoreVsBtn.setText("Learn more");
if (currentUserModel == null || currentUserModel.getVolunAccount() == null) {
volunDbListLay.setVisibility(View.GONE);
notVsAccWarning.setVisibility(View.VISIBLE);
} else {
volunDbListLay.setVisibility(View.VISIBLE);
notVsAccWarning.setVisibility(View.GONE);
loadVolunOrgList();
}
}
});
...
}
private void loadVolunOrgList() {
if (currentUserModel == null || currentUserModel.getVolunAccount() == null || currentUserModel.getOrgsList() == null || currentUserModel.getOrgsList().isEmpty()) {
return;
}
List<OrganisationModel> orgList = currentUserModel.getOrgDataList();
if (orgList.size() == 0) {
noVolunOrgsFound.setVisibility(View.VISIBLE);
return;
}
noVolunOrgsFound.setVisibility(View.GONE);
//Removing both noVolunOrgsFound.setVisibility() above from this function, the rest of the code then worked \/
UserIntegrAdapter orgAdapter = new UserIntegrAdapter(orgList, this, getSupportFragmentManager());
volunDbRecycler.setAdapter(orgAdapter);
volunDbRecycler.setLayoutManager(new LinearLayoutManager(ActivityUser.this));
}

Building a variable upon buttons to trigger listeners

I am breaking my head dealing with variables types in my app.
I try in a loop to increase a var and pass it to a listener, according to the name of the buttons defined in my XML layout.
I would like to start from "jeton1" to "jeton2","jeton3"...., but cannot manage to do that in my code (errors arising), the vars do not point to the buttons stored in the XML and not showing up when calling the buttons listeners.
I made a test with a defined array but the stuff failed.
Test code below made upon only one button.
A help would be greatly appreciated.
Here is my code
The XML layout :
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="20"
android:columnCount="9">
<Button
android:id="#+id/jeton1"
android:layout_column="0"
android:layout_row="0"
android:text="#string/boutona"
android:layout_height="88dp"
android:layout_width="88dp"
android:textSize="40sp"
android:backgroundTint="#eeceac"
android:textStyle="bold" />
Main Java :
public class MainActivity extends AppCompatActivity {
int i;
String jeton = "";
#SuppressLint("ClickableViewAccessibility")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Main loop
for (i = 1; i < 2; i++) {
jeton = "jeton" + i; // Should throw "jeton1", "jeton2".....
final Button jetonnew;
jetonnew = (Button) findViewById(R.id.jeton); // Error 'cannot resolve symbol
// jetonnew = (Button)findViewById(R.id.jeton+i);
// Step 4 Listener
jetonnew.setOnTouchListener(
new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
jetonnew.getBackground().setAlpha(0); // Crash app
jetonnew.setTextColor(Color.parseColor("#2aa17b"));
break;
}
return true;
}
});
}
}
}
Many thanks for your replies and suggestions.
If you have a fixed number of buttons, you can store an array of integers
int[] ids = {R.id.button1, R.id.button2, ...};
However, if you want to dynamically add buttons, you should try creating them programmatically
Button newButton = new Button(this);
or you can create some custom layout and inflate it
inflate(this, R.layout.customLayout, null);
Keep in mind that R.id.someId returns and integer not a string so you cannot append to it. Also adding a string after id does not work for the same reason.
jetonnew = (Button)findViewById(R.id.jeton+i);
This portion of code does not work because R.id.jeton is a generated integer not a string.
You should consider using findViewByTag instead of findViewById. Your code will look like this:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="20"
android:columnCount="9">
<Button
android:tag="jeton1"
android:layout_column="0"
android:layout_row="0"
android:text="#string/boutona"
android:layout_height="88dp"
android:layout_width="88dp"
android:textSize="40sp"
android:backgroundTint="#eeceac"
android:textStyle="bold" />
So in your java file:
for (i = 1; i < 2; i++) {
jeton = "jeton" + i; // Should throw "jeton1", "jeton2".....
final Button jetonnew;
//findViewByTag here
jetonnew = (Button) findViewByTag(jeton)
Another way is to just iterate through GridLayout child, like this:
//suppose your gridLayout has id=#+id/gridparent
GridLayout gridParent = (GridLayout)findViewById(R.id.gridparent);
for(int index=0; index<gridParent.getChildCount(); ++index) {
Button nextButton = (Button)gridParent.getChildAt(index);
//attach listener here
}
You should create arrays of your grid button id's.
#IntegerRes int [] resourceButtonIds = new int[]{R.id.jeton1,R.id.jeton2};
Then modify the loop accordingly.
for (i = 0; i < resourceButtonIds.length; i++) {
final Button jetonnew;
jetonnew = (Button) findViewById(resourceButtonIds[i]);
// now set all your listeners
}
If you want to find the id of the button by using its name:
jetonnew = findViewById(getResources().getIdentifier("jeton" + i, "id", getPackageName()));

Ripple Effect On ImageView not triggering

I have a RecyclerView with ImageViews in each item.
I set onClickListener for the ImageViews in onBindViewHolder as follows:
holder.starIV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO: logic
}
});
The ripple effect worked fine until I added the following logic to onClick. This logic changes the Drawable for the ImageView.
holder.starIV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getId() == holder.starIV.getId()) {
ListItem clickedItem = mDataset.get(position);
ListItem updatedItem = new ListItem(clickedItem);
if (clickedItem.getStarState() == STAR_ON) {
updatedItem.setStarState(STAR_OFF);
updatedItem.setStarDrawable(
ContextCompat.getDrawable(
v.getContext(),R.drawable.ic_star_border_24px));
}
else if (clickedItem.getStarState() == STAR_OFF) {
updatedItem.setStarState(STAR_ON);
updatedItem.setStarDrawable(
ContextCompat.getDrawable(
v.getContext(),R.drawable.ic_star_24px));
}
mDataset.set(position,updatedItem);
notifyDataSetChanged();
}
}
});
Now, I get no ripple effect at all. Here's the XML for the ImageView:
<ImageView
android:id="#+id/list_item_star"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingLeft="4dp"
android:paddingRight="16dp"
android:src="#drawable/ic_star_border_24px"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:drawSelectorOnTop="true"
/>
The ripple effect works normally again when i comment out the logic part in onClick.
Have I implemented the above correctly?
What change would you suggest to get the ripple effect working correctly?
EDIT: It appears that changing the Drawable is interfering with the ripple animation. So i moved all the logic to an AsyncTask with a small delay to allow the animation to finish. This seems to work, but I feel this solution is not elegant. Here's the AsyncTask:
class DoLogix extends AsyncTask<Integer, Integer, Void> {
#Override
protected Void doInBackground(Integer... params) {
try{Thread.sleep(125);}catch (Exception e) {}
publishProgress(params[0]);
return null;
}
protected void onProgressUpdate(Integer... val) {
ListItem clickedItem = mDataset.get(val[0]);
ListItem updatedItem = new ListItem(clickedItem);
if (clickedItem.getStarState() == STAR_ON) {
updatedItem.setStarState(STAR_OFF);
updatedItem.setStarDrawable(starBorder);
}
else if (clickedItem.getStarState() == STAR_OFF) {
updatedItem.setStarState(STAR_ON);
updatedItem.setStarDrawable(star);
}
mDataset.set(val[0],updatedItem);
notifyDataSetChanged();
}
}
u can set a ripple drawable as the foreground of ur imageview.
add below code to your parent layout
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"

My layout values are not updating

So I have Track.java with its layout that is just suppose to show me updated GPS coordinates with myTextLat and myTextLong. And a MainActivity.java that has a method locationChanged that spits out new GPS data as it becomes available, but for whatever reason my layout is not updating with the new data, despite being able to see new coordinate data coming out of locationChanged in the system out. I can statically set them by doing a settext in onCreateView, but for some reason they will not update through setMyCoords. Can someone help me figure out why the data, when available, is not being passed into my layout? Is there another, better, way to pass data from the activity to objects in a fragment so they are always updatedy? Thanks.
MainActivity's locationChanged
#Override
public void locationChanged(double longitude, double latitude) {
try {
System.out.println("Longitude: " +longitude);
System.out.println("Latitude: " + latitude);
Track f = new Track();
f.setMyCoords(latitude,longitude);
} catch (NullPointerException e) {
}
Track.java
package "";
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class Track extends Fragment {
MiddleMan mCallBack;
TextView myTextLat;
TextView myTextLong;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallBack = (MiddleMan) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ReqestConnect");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.track_display, container, false);
myTextLat = (TextView) view.findViewById(R.id.textView_lat);
myTextLong = (TextView) view.findViewById(R.id.textView_long);
mCallBack.DisplayHome();
return view;
}
public void setMyCoords(final double slat, final double slong) {
myTextLat.setText(Double.toString(slat));
myTextLong.setText(Double.toString(slong));
}
}
This might also help. Each fragment replaces a framelayout in MainActivity when called. it looks like this.
#Override
public void ShiftView(Object obj) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.activity_main_framelayout, (Fragment) obj);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
With the actual call being something like.
Track f = new Track();
ShiftView(f);
Conclusion
With Joel Min's help I was able to come to the conclusion to my problem. I only have one activity but use several fragments to take on the role of having multiple activities, from the viewpoint of the user:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:orientation="vertical" tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#27b"
android:layout_weight=".04">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/activity_main_framelayout">
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight=".9"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1B5F96"
android:layout_weight=".9"
android:id="#+id/activity_main_status_title"
android:text="#string/activity_main_status_title"
tools:ignore="NestedWeights" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AD3333"
android:layout_weight=".15"
android:id="#+id/activity_main_status_value"
android:text="#string/activity_main_status_value"/>
</LinearLayout>
</LinearLayout>
The Framelayout eats the vast majority of the display, with ShiftView basically swapping in the fragment's layout by calling on the fragment class, as stated above. The problem? Shifting views is done by the onOptionsitemSelected method where each entry essentially looks like this:
if (id == R.id.action_track_display) {
Track f = new Track();
ShiftView(f);
return true;
Which has been fine for the project up to this point, however, Track.java needs to do something the other classes don't, it needs to receive and retain gps data regardless of where the user is in the app. My menu produces a new Track object, my locationChanged method produces a new Track object every time the location changes [which is a lot of objects], none of the objects are the same and none are connected to MainActivity.java in any way. The result, you get a crashless app that has a Track object's layout visible to the user that never updates and a series of background Track objects that exist for a fraction of a second, each containing one set of gps points. The fix, pretty simple:
MainActivity.java
public class MainActivity extends AppCompatActivity {
Track my_track;
...
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_track_display) {
if (my_track==null) {
my_track = new Track();
}
ShiftView((my_track);
return true;
}
....
#Override
public void locationChanged(final double longitude, final double latitude) {
try {
System.out.println("Main-Longitude: " +longitude);
System.out.println("Main-Latitude: " + latitude);
runOnUiThread(new Runnable() {
#Override
public void run() {
my_track.setMyCoords(latitude,longitude);
}
});
} catch (NullPointerException e) {}
Each fragment replaces a framelayout in MainActivity when called
But you are calling f.setMyCoords(latitude,longitude); after the fragment has been created and returned to the main UI as it is (without setMyCoords applied). So move f.setMyCoords(latitude,longitude); from your locationChanged method to ShitView method. Of course then you will need to have global variables tempLong and tempLat to temporarily store the longitude and latitude values in locationChanged, and access them in ShiftView. Below is the modified code:
private double tempLong, tempLat; //declare it at class level
#Override
public void locationChanged(double longitude, double latitude) {
try {
System.out.println("Longitude: " +longitude);
System.out.println("Latitude: " + latitude);
Track f = new Track();
tempLong = longitude;
tempLat = latitude;
} catch (NullPointerException e) {
}
#Override
public void ShiftView(Object obj) {
(Fragment) obj.setMyCoords(tempLat, tempLong);
//if above line causes error try the line below;
//Track f = (Fragment) obj;
//f.setMyCoords(tempLat, tempLong);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.activity_main_framelayout, (Fragment) obj);
//ft.replace(R.id.activity_main_framelayout, f);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
I cannot guarantee the above code would work because I don't have the full code. But basically you either need to set the longitude and latitude before the fragment transition in main activity occurs, or set a callback mechanism in your setMyCoords method so when it's called it calls back the main activity to update the textviews with new long and lat.
It seems that you are calling setText from NOT ui thread.
Consider calling it in UI thread using smth like this:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
myTextLat.setText(Double.toString(slat));
myTextLong.setText(Double.toString(slong));
}
});

Creating a Singleton CountDownTimer in Android

I'm a beginner in android and I've written an activity. It contains a CountDownTimer that counts down from a particular value. It also contains a Button that loads text information and a textview to display count.
Below is the code for Activity1:
public class Screen extends Activity1 implements OnClickListener {
private static final int MILLIS_PER_SECOND = 1000;
private static final int SECONDS_TO_COUNTDOWN = 1;
TextView Time;
int totaltime;
Button startTimer, howTo, pause;
protected CountDownTimer MyTimer;
int PracticeCount;
long tot;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.pushupscreen);
getRefs();
getSpeed();
getCount();
setTotalTime();
startTimer.setOnClickListener(this);
pause.setOnClickListener(this);
}
private void getRefs() {
// Initialize layout resources
Time = (TextView) findViewById(R.id.tvTime);
startTimer = (Button) findViewById(R.id.bStart);
howTo = (Button) findViewById(R.id.btHowTo);
pause = (Button) findViewById(R.id.bPause);
howTo.setOnClickListener(this);
}
private void getTheCount() {
//get count from SharedPreferences
}
private void getSpeed() {
//get speed from SharedPreferences
}
private void setCount(){
totalTime=speed*count;}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v == startTimer) {
try {
showTimer(time);
} catch (NumberFormatException e) {
// method ignores invalid (non-integer) input and waits
// for something it cant use
}
} else if (v == pause) {
MyTimer.cancel();
Timer.setText("Resume");
} else if (v == howTo) {
//Launch screen containing information
}
}
private void showTimer(long time) {
if (MyTimer != null) {
MyTimer.cancel();
}
MyTimer = new CountDownTimer(tot2, MILLIS_PER_SECOND) {
#Override
public void onTick(long millisUntilFinished) {
tot = millisUntilFinished;
long seconds = millisUntilFinished / 1000;
Time.setText(String.format("%02d", seconds / 60) + ":"
+ String.format("%02d", seconds % 60));
}
#Override
public void onFinish() {
Time.setText("KABOOM!");
}
}.start();
}
}
And here is the layout file for this:
<TextView
android:id="#+id/tvTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:gravity="center"
android:padding="10dip"
android:text="#string/starttime"
android:textSize="60sp" />
<Button
android:id="#+id/bStart"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_above="#+id/tvTime"
android:text="Start" />
<Button
android:id="#+id/bPause"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_above="#+id/tvTime"
android:layout_toRightOf="#+id/btHowTo"
android:text="Pause" />
<TextView
android:id="#+id/tvCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/btHowTo"
android:layout_centerHorizontal="true"
android:layout_marginTop="39dp"
android:text="25"
android:textSize="80sp"
android:textAlignment="center"/>
My questions:
1.How do I create 4 activities that use the same layout and the same timer? Each Activity loads a different content in the textview and a different screen on the click of HowTo button.
2.How can Activity1 be designed to run for 1/4th the time set and pass remaining time to Activity2? Is it possible?
I would really appreciate any help and advice that you can provide. Thanks.
A couple things here.
Its very easy to re-use layouts. In each activity's onCreate you would just call:
setContentView(R.layout.pushupscreen); The pushupscreen.xml file can be shared across all activities this way.
What you probably want to do is persist a timestamp to some common data source for all the activities. This could be a write to a SharedPreferences file: Documentation here. Then as each activity resumes, check how much time has already passed by comparing this timestamp to the current timestamp. You could also pass the timestamp as an extra in the intent to start up the subsequent activities. The documentation for that can be found here and here
You could make a custom control, which is basically a new class which inherits some other control's class (for example a LinearLayout or a RelativeLayout). You could then load a view's XML to your new layout or programmatically create new controls inside your control. More info here:
Custom components in Android
After a 1/4 of your countdown period, you can create and send an Intent to start a new activity in the onTick method. You can also put the remaining 3/4 as a millisecond value (of type long) in an intent extra. You can then obtain this value in the new activity and invoke a custom CountDownTimer child there for the rest of your countdown. Then you can finally execute what you wish after the countdown is done in the onFinish() method.

Categories