You may have observed the fantastic gesture controlled Gallery app in Android devices which can zoom in or out on pinching and swipe it left or right changes the currently displayed image.
In today's tutorial we're going to build a similar functionality for a TextView. And, we would go further by implementing gesture based responsiveness to other (similar) widgets too.
For our example let's have an Activity that allows reading comments from different users in such a manner that swiping left or right would change the comments being displayed. Here is a rough UI:
Now, we define the UI for the Reader in an XML layout;
Coordinate Geometry is used in order to fetch and make an understanding of touch inputs. The device's screen is considered a 2 Dimensional coordinate map. Each touch event is registered by returning its corresponding X,Y coordinate on the device. The devices on which your app will run are going to have varied sizes and resolutions. Thus, relative coordinates are used or as in this case, only the difference (called delta by physicists and mathematically inclined people) in the coordinates is enough for determining fling gestures. More details is commented in source code, if you wish to learn more.
Implementing Pinch to Zoom & Fling to navigate is a tough combination. The actual events supported by the Android SDK and corresponding touch-events can cause triggering of one another due to the ambiguous nature of our input. There are two solutions to it; Either we can check for the number of touched points at that time to discern the multi-touch pinch and zoom in contrast to the single touch flings. Or, we can provide a toggle switch to the user to activate fling navigation or set the text view font size.
Reader Modes are required in order to lock swipe or fling navigation, which can be triggered accidentally while reading. This navigation lock can be provided as a toggle switch on the Action Bar, double tap or even long press and any other ways you can think of. But, such locking mechanisms should be tested well to understand its efficiency in avoiding accidental gestures and navigation interrupting the user's reading experience.
I have used three Reader modes - Navigation, Reader and Visibility.
I have used the third party components ColorPicker and ColorPickerDialog from this GitHub repository. It is a very beautiful dialog to let users pick color. I have used this to allow users of the Reader to set the fore-color and the background color as per their taste and ease of reading. The colors of the Reader is editable by invoking the Color Picker Dialog from the drop down menu on the Action Bar. You might want to persist the user set font size and colors in order to save their preferences when they use the app again (Use Shared Preferences. Here's a tutorial - Link).
Finally, let's write the Reader's MainActivity defining the gesture listener and behavior of the app.
In today's tutorial we're going to build a similar functionality for a TextView. And, we would go further by implementing gesture based responsiveness to other (similar) widgets too.
For our example let's have an Activity that allows reading comments from different users in such a manner that swiping left or right would change the comments being displayed. Here is a rough UI:
Now, that we have defined our basic behavior and display of the UI, we'll do some more object oriented designing regarding our data. As we are going to use a set of information, it's best to use a custom data object or class in which we define all our attributes/fields and how to handle them.
Note: This project uses some custom assets like, Circular Progress Bar. And, third party components like Piotr Adamus's Color Picker and Color Picker Dialog (Made available under Apache License Version 2.0). All asset files and third party files are bundled with the source code available for download at the end of the tutorial, no need to fret.
As my current problem statement deals with showing Comments. Let's make a class called Comments. You can use any other similar data representation class as per you requirements. Here is the Java class -
public class Comments { private ArrayList<String> CommentBy = new ArrayList<String>(); private ArrayList<String> CommentData = new ArrayList<String>(); //A constructor to be used for actual initialization of object from information fetched from //other sources like a web-service or on device storage (SQLite database). public Comments(ArrayList<String> commentBy, ArrayList<String> commentData) { super(); CommentBy = commentBy; CommentData = commentData; } //My impromptu constructor used to initialize Comments object with a set of preset sample comments //Make sure you remove this constructor when using for actual app. public Comments() { super(); CommentBy.clear(); CommentData.clear(); //Standard Blue User CommentBy.add("Average_Joe"); CommentData.add("I love seeing movies made from Comics characters!!"); //Standard Black User CommentBy.add("Naseem"); CommentData.add("Ssh! This is just a tutorial/demo dont attract attention here."+"\n\n"+"You don't know what kind of people will be attracted towards this discussion on the web."); //Joker CommentBy.add("Joker"); CommentData.add("Ooh! A new discussion! Do you want to hear a joke?"); //Batman CommentBy.add("Batman"); CommentData.add("Where did you get your hands on an Android phone in Arkham?"); //Joker CommentBy.add("Joker"); CommentData.add("@Batman Party pooper! I was just about to crack a joke. And, how did you know I am here on the web?"); //Batman CommentBy.add("Batman"); CommentData.add("Because I'm Batman! And, I'm the world's greatest detective; I figured it out."+"/n/n"+"@Joker Now, tell me where'd you get an Android device in a padded cell?"); //Standard Blue User CommentBy.add("Average_Joe"); CommentData.add("@Batman Don't you have, like, a big Batcomputer you use to find out criminals?"); //Standard Black User CommentBy.add("Naseem"); CommentData.add("@Batman Yes. I'm interested in how did you find out Joker was trolling in this discussion? I'd like to know your technique."); //Deadpool CommentBy.add("Deadpool!"); CommentData.add("Hey Batman, you here? I just read your status on Facebook that you were on this discussion app. Thought I'd join in."+"\n\n\n"+"@Naseem Oh! Don't bother knowing his technique. He left me mid chat on FB when when Joker updated his status on Twitter talking about a new discussion app he had found to troll for the Lulz."); } public String getCommentBy(int position) { return CommentBy.get(position); } public void setCommentBy(String commentBy, int position) { CommentBy.add(position, commentBy); } public String getCommentData(int position) { return CommentData.get(position); } public void setCommentData(String commentData, int position) { CommentData.add(position, commentData); } public int numberOfComments() { return CommentBy.size(); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <RelativeLayout android:id="@+id/top_frame" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/imageView1" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:padding="5dp" android:scaleType="fitCenter" android:adjustViewBounds="true" /> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_toRightOf="@+id/imageView1" android:padding="5dp" android:text="User Name" android:textColor="#087CCD" /> </RelativeLayout> <RelativeLayout android:id="@+id/bottom_frame" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" > <TextView android:id="@+id/mytv1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:text="This is my sample text for pinch zoom demo, you can zoom in and out using pinch zoom, thanks" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:scrollbars= "vertical" android:singleLine= "false" android:maxLines="6" /> <EditText android:id="@+id/mytv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:text="This is my sample text for pinch zoom demo, you can zoom in and out using pinch zoom, thanks" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:scrollbars= "vertical" android:inputType="none" android:cursorVisible="false" android:minLines="5" android:background="@drawable/rounded_edittext" android:maxLines="6" /> <ImageButton android:id="@+id/imButton1" android:layout_width="90dp" android:layout_height="90dp" android:layout_marginRight="15dp" android:layout_marginBottom="15dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:scaleType="centerInside" android:adjustViewBounds="true" android:background="@drawable/reader_icon" /> <ProgressBar android:id="@+id/progressBarToday" style="?android:attr/progressBarStyleHorizontal" android:layout_width="90dp" android:layout_height="90dp" android:layout_marginRight="15dp" android:layout_marginBottom="15dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:indeterminate="false" android:max="30" android:progress="29" android:progressDrawable="@drawable/circular_progress_bar" /> </RelativeLayout> </LinearLayout>
Important things to consider:
Before we go further let's get into the real world issues we need to consider in defining the behavior of the gesture aware TextView, required functionality of the Reader and challenges that come with it.Coordinate Geometry is used in order to fetch and make an understanding of touch inputs. The device's screen is considered a 2 Dimensional coordinate map. Each touch event is registered by returning its corresponding X,Y coordinate on the device. The devices on which your app will run are going to have varied sizes and resolutions. Thus, relative coordinates are used or as in this case, only the difference (called delta by physicists and mathematically inclined people) in the coordinates is enough for determining fling gestures. More details is commented in source code, if you wish to learn more.
Implementing Pinch to Zoom & Fling to navigate is a tough combination. The actual events supported by the Android SDK and corresponding touch-events can cause triggering of one another due to the ambiguous nature of our input. There are two solutions to it; Either we can check for the number of touched points at that time to discern the multi-touch pinch and zoom in contrast to the single touch flings. Or, we can provide a toggle switch to the user to activate fling navigation or set the text view font size.
Reader Modes are required in order to lock swipe or fling navigation, which can be triggered accidentally while reading. This navigation lock can be provided as a toggle switch on the Action Bar, double tap or even long press and any other ways you can think of. But, such locking mechanisms should be tested well to understand its efficiency in avoiding accidental gestures and navigation interrupting the user's reading experience.
I have used three Reader modes - Navigation, Reader and Visibility.
- The Navigation mode only allows the user to quickly swipe/fling through the Comments.
- The Reader mode locks fling navigation into place and allows vertical scrolling of Edit Text which replaces the Text View (else use a scroll-able Text View, not implemented here). The Reader mode should be accompanied with a set of navigation buttons to navigate between the Comments without using gestures, which is out of the scope of this tutorial.
- And, the Visibility mode supports the pinch to zoom feature to setup the font size to a comfortable size and then automatically switch to reader mode after some time.
I have used the third party components ColorPicker and ColorPickerDialog from this GitHub repository. It is a very beautiful dialog to let users pick color. I have used this to allow users of the Reader to set the fore-color and the background color as per their taste and ease of reading. The colors of the Reader is editable by invoking the Color Picker Dialog from the drop down menu on the Action Bar. You might want to persist the user set font size and colors in order to save their preferences when they use the app again (Use Shared Preferences. Here's a tutorial - Link).
Finally, let's write the Reader's MainActivity defining the gesture listener and behavior of the app.
public class NewTextViewZoom extends Activity implements GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener { private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; String MODE = "NAVIGATION"; /*Modes: {NAVIGATION, READER, VISIBILITY} * *NAVIGATION - Only detect gestures like swipes in left, right, up and down directions. *Navigation mode allows the user to switch between comments. * *READER - Detect gestures to only allow scroll on EditText for very long texts. *Makes it easy to read long texts by locking all other gesture commands in order to *avoid accidental touches and altering of current reading experience. * * VISIBILITY - Only detect pinch in and pinch out to zoom in or out on text. * Visibility mode must be accompanied with a timer to toggle off this mode such as to * avoid resizing text on accidentally touching the screen, which happens a lot on large * amount of texts. * */ Comments comment; //Comments data-object. You can use any of your own data objects as per requirement. int pos = 0; final static float STEP = 200; TextView mytv; TextView ordinaryTV; int forecolor = Color.BLACK; int backcolor = android.R.color.background_light; TextView nametv; ImageView useriv; View readerscreen; View titlescreen; float mRatio = 1.0f; int mBaseDist; float mBaseRatio; private float flingMin = 100; private float velocityMin = 100; Context con; ImageButton imButton; private ActionBar actionBar; private final long startTime = 30000; private final long interval = 1000; private ParisCountDownTimer countDownTimer; private ProgressBar countdown_progress; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.zoomtext_layout); actionBar = getActionBar(); actionBar.setDisplayShowTitleEnabled(false); LayoutInflater inflater = LayoutInflater.from(this); View customView = inflater.inflate(R.layout.bar, null); TextView titleTV = (TextView) customView.findViewById(R.id.title); titleTV.setSelected(true); actionBar.setCustomView(customView); actionBar.setDisplayShowCustomEnabled(true); titleTV.setText("Gesture Reader"); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double tap // listener. mDetector.setOnDoubleTapListener(this); comment = new Comments(); con = this; mytv = (TextView) findViewById(R.id.mytv); ordinaryTV = (TextView) findViewById(R.id.mytv1); nametv = (TextView) findViewById(R.id.name); useriv = (ImageView) findViewById(R.id.imageView1); readerscreen = (View) findViewById(R.id.bottom_frame); titlescreen = (View) findViewById(R.id.top_frame); countdown_progress = (ProgressBar) findViewById(R.id.progressBarToday); countdown_progress.setVisibility(View.INVISIBLE); mytv.setTextSize(mRatio + 26); mytv.setVisibility(View.INVISIBLE); ordinaryTV.setTextSize(mRatio + 26); ordinaryTV.setVisibility(View.VISIBLE); //Dedicated method to switch colors on text which is set as Spannable Text. setColoredText(comment.getCommentData(pos),forecolor,backcolor); nametv.setText(comment.getCommentBy(pos)); imButton = (ImageButton) findViewById(R.id.imButton1); imButton.setBackgroundResource(R.drawable.navigation_icon); imButton.setOnClickListener(new OnClickListener()
{ @Override public void onClick(View v) { if(MODE == "NAVIGATION")//Gesture based Navigation is On { MODE = "READER"; ordinaryTV.setVisibility(View.INVISIBLE); mytv.setVisibility(View.VISIBLE); imButton.setBackgroundResource(R.drawable.reader_icon); Toast.makeText(con, "Reader Mode Activated.", Toast.LENGTH_LONG).show(); } else if(MODE == "VISIBILITY") { MODE = "READER"; ordinaryTV.setVisibility(View.INVISIBLE); mytv.setVisibility(View.VISIBLE); imButton.setBackgroundResource(R.drawable.reader_icon); countdown_progress.setVisibility(View.INVISIBLE); } else { MODE = "NAVIGATION"; ordinaryTV.setVisibility(View.VISIBLE); mytv.setVisibility(View.INVISIBLE); imButton.setBackgroundResource(R.drawable.navigation_icon); Toast.makeText(con, "Navigation Mode Activated.", Toast.LENGTH_LONG).show(); } } }); imButton.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { if(MODE == "VISIBILITY") { MODE = "READER"; ordinaryTV.setVisibility(View.INVISIBLE); mytv.setVisibility(View.VISIBLE); imButton.setBackgroundResource(R.drawable.reader_icon); countDownTimer.cancel(); countdown_progress.setVisibility(View.INVISIBLE); //Research on font sizes float tsize = mytv.getTextSize(); float tmratio = mRatio; String stats = "Text Size = "+tsize+" and mRatio = "+tmratio; mytv.setText(stats); } else { MODE = "VISIBILITY"; imButton.setBackgroundResource(R.drawable.navigation_icon); ordinaryTV.setVisibility(View.VISIBLE); mytv.setVisibility(View.INVISIBLE); //***Setup Countdown Timer***// countdown_progress.setVisibility(View.VISIBLE); countdown_progress.setProgress(countdown_progress.getMax()); countDownTimer = new ParisCountDownTimer(startTime, interval); countDownTimer.start(); //***Setup Countdown Timer***// } return true; } }); } @Override public boolean onTouchEvent(MotionEvent event)
{ if(MODE.equalsIgnoreCase("VISIBILITY")) //check if navigation is on. { if (event.getPointerCount() == 2) { int action = event.getAction(); int pureaction = action & MotionEvent.ACTION_MASK; if (pureaction == MotionEvent.ACTION_POINTER_DOWN)
{ mBaseDist = getDistance(event); mBaseRatio = mRatio; }
else
{ float delta = (getDistance(event) - mBaseDist) / STEP; float multi = (float) Math.pow(2, delta); mRatio = Math.min(1024.0f, Math.max(0.1f, mBaseRatio * multi)); mytv.setTextSize(mRatio + 26); ordinaryTV.setTextSize(mRatio + 26); } return false; } } else if(MODE.equalsIgnoreCase("NAVIGATION")) { this.mDetector.onTouchEvent(event); return super.onTouchEvent(event); } return false; } @Override public boolean onDown(MotionEvent event) { //No onDown event needed return true; }
/* * onFling happens to be the most important gesture control available. * It returns X,Y coordinates of the first touch event and then the last touch event * when the user lifts his/ger finger. * * We use this X,Y coordinates for calculating the distance and velocity of fling. * * Accidental flings and touches tend to be of smaller coordinate differences and * generally of lower velocity. * * Adjust the constraints as per your requirement and on device touch experience. * You may need to save a log of possible gestures in your first prototype and then * judge the flingMin and velocityMin parameters or even velocityMax and flingMax. * */ @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { //calculate the change in X position within the fling gesture float horizontalDiff = event2.getX() - event1.getX(); //calculate the change in Y position within the fling gesture float verticalDiff = event2.getY() - event1.getY(); float absHDiff = Math.abs(horizontalDiff); float absVDiff = Math.abs(verticalDiff); float absVelocityX = Math.abs(velocityX); float absVelocityY = Math.abs(velocityY); /* * Determining Fling directions and corresponding behavior. * * I have used a standard English book behavior, where the Comments change * as per the pages of a book. Right to left denotes a page turn or Next_Item. * Left to right denotes a previous page turn or Previous_Item. * * Flings should be accompanied by audio cues (like sound of page turning etc) * such that the progression or change of data can be affirmed without distracting * from a decent reading experience. * * Flings Up and Down have been left out for the sake of understanding. They too * can be used to pull down the topic of discussion or a tool bar can be pulled * up from the bottom of the screen as per your requirement. * * An image tool bar example is available at - * http://stackoverflow.com/questions/26695864/how-to-add-a-one-side-border-and-background-into-one-drawable/26696547#26696547 * * * */ if(absHDiff>absVDiff && absHDiff>flingMin && absVelocityX>velocityMin) //Horizontal Flings { if(horizontalDiff>0) { //LEFT->RIGHT //Behavior = Show Previous Comment if(pos>0) { pos--; setColoredText(comment.getCommentData(pos),forecolor,backcolor); nametv.setText(comment.getCommentBy(pos)); } else { //If first comment is reached then alert the user, if possible even ring a bell //or some audio cue. Toast.makeText(con, "Reached first comment.", Toast.LENGTH_LONG).show(); } } else { //LEFT<-RIGHT //Behavior = Show Next Comment; if(pos<comment.numberOfComments()-1) { pos++; setColoredText(comment.getCommentData(pos),forecolor,backcolor); nametv.setText(comment.getCommentBy(pos)); } else { Toast.makeText(con, "No more comments.", Toast.LENGTH_LONG).show(); } } } else if(absVDiff>flingMin && absVelocityY>velocityMin) //Vertical Flings { if(verticalDiff>0) { //UP->DOWN //Show Discussion Frame in Reader Toast.makeText(con, "No behavior defined.", Toast.LENGTH_LONG).show(); } else { //DOWN->UP //Hide Discussion Frame from Reader if Visible //Suggestion: Show Comment at Current State such that the user does not //loose his/her place in the list of comments they are reading. Toast.makeText(con, "No behavior defined.", Toast.LENGTH_LONG).show(); } } return true; } @Override public void onLongPress(MotionEvent event) { //Not required here. } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //Not required here. return true; } @Override public void onShowPress(MotionEvent event) { //Not required here. } @Override public boolean onSingleTapUp(MotionEvent event) { //Not required here. return true; } @Override public boolean onDoubleTap(MotionEvent event) { //Not required here. return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { //Not required here. return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { //Not required here. return true; }
/* * In the methods below the TextView and the EditText have their UI defined. * Each has to be set with the same foreground and background color in order to * exhibit a consistent color scheme as set by the user. * * You may want to persist the color settings made by the user using Shared Preferences. * A tutorial on using Shared Preferences can be found here - * http://www.coders-hub.com/2013/10/how-to-use-shared-preferences-in-android.html#.VTIk7SGqqko * * */ private void setColoredText(String fulltext, int forecolor1, int backcolor1) { mytv.setText(fulltext, TextView.BufferType.SPANNABLE); Spannable str = (Spannable) mytv.getText(); str.setSpan(new ForegroundColorSpan(forecolor1), 0, mytv.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(backcolor1), 0, mytv.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ordinaryTV.setText(fulltext, TextView.BufferType.SPANNABLE); str = (Spannable) ordinaryTV.getText(); str.setSpan(new ForegroundColorSpan(forecolor1), 0, ordinaryTV.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(backcolor1), 0, ordinaryTV.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } private void setColoredText( int forecolor1, int backcolor1) { Spannable str = (Spannable) mytv.getText(); str.setSpan(new ForegroundColorSpan(forecolor1), 0, mytv.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(backcolor1), 0, mytv.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str = (Spannable) ordinaryTV.getText(); str.setSpan(new ForegroundColorSpan(forecolor1), 0, ordinaryTV.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(backcolor1), 0, ordinaryTV.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); }
/* * A little bit of coordinate geometry. * The mathematically inclined would recognize the below formula as the Distance Formula or Pythagorean * Theorem. Distance formula is used to calculate the distance between two points in a coordinate space. * * There are 3-Dimensional, 2-Dimensional and multi-dimensional distance formulas as per the * nature of their coordinate space or environment. While, the Device screen is only 2-Dimensional * and flat the 2D Distance Equation is used. * * More about Distance formula here - http://en.wikipedia.org/wiki/Distance * */ int getDistance(MotionEvent event) { int dx = (int) (event.getX(0) - event.getX(1)); int dy = (int) (event.getY(0) - event.getY(1)); return (int) (Math.sqrt(dx * dx + dy * dy)); } /* * Third Party components built by Piotr Adamus are used here; * * The ColorPicker and ColorPickerDialog. * * More about it here - https://github.com/chiralcode/Android-Color-Picker/ * * */ private void showColorPickerDialogDemo(final int inputtype) { int initialColor = Color.WHITE; ColorPickerDialog colorPickerDialog = new ColorPickerDialog(this, initialColor, new OnColorSelectedListener() { @Override public void onColorSelected(int color) { if(inputtype == 0) { forecolor = color; } else { backcolor = color; readerscreen.setBackgroundColor(color); titlescreen.setBackgroundColor(color); } setColoredText(forecolor, backcolor); } }); colorPickerDialog.show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.reader_menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.forecolor: showColorPickerDialogDemo(0); return true; case R.id.backcolor: showColorPickerDialogDemo(1); return true; } return false; }
/*My custom Count down timer which sets the behavior of the circular/ring progress bar *and reader mode switching behavior. * *Circular/Ring progress bar is discussed in this tutorial on my personal blog, being *trivial to the scope of Coder's Hub. */ public class ParisCountDownTimer extends CountDownTimer { public ParisCountDownTimer(long startTime, long interval) { super(startTime, interval); } @Override public void onFinish() { MODE = "READER"; imButton.setBackgroundResource(R.drawable.reader_icon); countdown_progress.setVisibility(View.INVISIBLE); ordinaryTV.setVisibility(View.INVISIBLE); mytv.setVisibility(View.VISIBLE); } @Override public void onTick(long millisUntilFinished) { countdown_progress.setProgress(countdown_progress.getProgress()-1); } } }
Source Code
Download project source code and sample apk from here.
Related Programs:-
★ Add Google AdMob Ads in Android App & Earn Money
★ Add Facebook SDK in Android Application
★ Advance Android Google Map 2 Tutorial with Examples - Part 1
★ Start Working on Linphone Android
★ Sign Android Application and Publish on Google Play
Related Programs:-
★ Add Google AdMob Ads in Android App & Earn Money
★ Add Facebook SDK in Android Application
★ Advance Android Google Map 2 Tutorial with Examples - Part 1
★ Start Working on Linphone Android
★ Sign Android Application and Publish on Google Play
A very good tutorial. Thanks
ReplyDeleteYou are welcome!
Deletehelpful thanks . .
ReplyDelete