Is the SwitchPreference introduced in ICS compatible in the android-support-v4 library? I'm trying to update some old projects and would like to use SwitchPreferences if possible.
I know I can make a separate resource file to distinguish the API version, but I'd like to avoid that if at all possible.
Is the SwitchPreference introduced in ICS compatible in the android-support-v4 library?
No, sorry.
However, it shouldn't be too tricky to backport it, if someone hasn't already.
Actually, it may be a bit of a pain, since it also requires a backport of Switch, and backporting widgets is sometimes troublesome because they frequently use package-private methods that backports cannot access.
I know I can make a separate resource file to distinguish the API version, but I'd like to avoid that if at all possible.
Well, that would certainly be way simpler than the alternatives:
the aforementioned backport
creating some sort of alias Preference mechanism that allows you to use SwitchPreference on newer devices and CheckBoxPreference on older devices with only one resource file
android-switch-backport has a SwitchPreference which works on Android 2.1+.
https://github.com/BoD/android-switch-backport
I've tried every solution that i found but non of them were fit my needs, so i created my own widget wich is used ObjectAnimator from nineOld compatibility lib and works pretty fine on any android API.
import android.widget.RelativeLayout;
import com.myapp.utilities.AppUtils;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ObjectAnimator;
public class SwitchButton extends RelativeLayout {
public static final int TEXT_SIZE = 11;
public float HANDLE_SHIFT = -40f;
public float TEXT_RIGHT_SHIFT = 40f;
public static int BUTTON_ID = 0x00009999;
public static int TEXT_ID = 0x00008888;
private Button handleButton;
private RoboTextView textView;
private boolean switchEnabled;
private String yesStr;
private String noStr;
private int TEXT_LEFT_PADDING = 13;
private ObjectAnimator animateHandleLeftShift;
private ObjectAnimator animateHandleRightShift;
private int HANDLE_BUTTON_HEIGHT = 22;
private int HANDLE_BUTTON_WIDTH = 42;
private ObjectAnimator animateTextLeftShift;
private ObjectAnimator animateTextRightShift;
public SwitchButton(Context context) {
super(context);
onCreate(context);
}
private void onCreate(Context context) {
float density = context.getResources().getDisplayMetrics().density;
TEXT_LEFT_PADDING *= density;
HANDLE_BUTTON_HEIGHT *= density;
HANDLE_BUTTON_WIDTH *= density;
HANDLE_SHIFT *= density;
TEXT_RIGHT_SHIFT *= density;
yesStr = getContext().getString(R.string.yes).toUpperCase();
noStr = getContext().getString(R.string.no).toUpperCase();
{// Button
handleButton = new Button(getContext());
RelativeLayout.LayoutParams buttonParams = new LayoutParams(HANDLE_BUTTON_WIDTH, HANDLE_BUTTON_HEIGHT);
buttonParams.addRule(RelativeLayout.CENTER_VERTICAL);
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
handleButton.setBackgroundResource(R.drawable.button_switch_handle_selector);
handleButton.setId(BUTTON_ID);
addView(handleButton, buttonParams);
}
{// Text
textView = new RoboTextView(getContext());
LayoutParams textParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
textParams.addRule(RelativeLayout.CENTER_VERTICAL);
textView.setText(yesStr);
textView.setTextColor(getContext().getResources().getColor(R.color.new_normal_gray));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE);
textView.setPadding(TEXT_LEFT_PADDING, 0, 0, 0);
textView.setFont(RoboTextView.ROBOTO_BOLD_FONT);
textView.setId(TEXT_ID);
float shadowRadius = 0.5f ;
float shadowDx = 0;
float shadowDy = 1;
textView.setShadowLayer(shadowRadius, shadowDx, shadowDy, Color.BLACK);
addView(textView, textParams);
}
initFlipAnimation();
}
#Override
public void setOnClickListener(OnClickListener l) {
handleButton.setOnClickListener(l);
textView.setOnClickListener(l);
}
public void toggle(View view){
if (AppUtils.HONEYCOMB_PLUS_API && view.getId() == TEXT_ID) { // ignore text clicks
return;
}
switchEnabled = !switchEnabled;
if (switchEnabled) {
// animate handle to the left
animateHandleLeftShift.start();
animateTextLeftShift.start();
textView.setText(noStr);
} else {
animateHandleRightShift.start();
animateTextRightShift.start();
textView.setText(yesStr);
}
}
private android.view.animation.Interpolator accelerator = new LinearInterpolator();
private static final int DURATION = 70;
private void initFlipAnimation() {
animateHandleLeftShift = ObjectAnimator.ofFloat(handleButton, "translationX", 0f, HANDLE_SHIFT);
animateHandleLeftShift.setDuration(DURATION);
animateHandleLeftShift.setInterpolator(accelerator);
animateHandleRightShift = ObjectAnimator.ofFloat(handleButton, "translationX", HANDLE_SHIFT, 0f);
animateHandleRightShift.setDuration(DURATION);
animateHandleRightShift.setInterpolator(accelerator);
animateHandleLeftShift.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator anim) {
// TODO
}
});
animateTextLeftShift = ObjectAnimator.ofFloat(textView, "translationX", 0f, TEXT_RIGHT_SHIFT);
animateTextLeftShift.setDuration(DURATION);
animateTextLeftShift.setInterpolator(accelerator);
animateTextRightShift = ObjectAnimator.ofFloat(textView, "translationX", TEXT_RIGHT_SHIFT, 0f);
animateTextRightShift.setDuration(DURATION);
animateTextRightShift.setInterpolator(accelerator);
}
}
In XML
<com.chess.SwitchButton
android:id="#+id/ratedGameSwitch"
android:layout_width="#dimen/button_switch_width"
android:layout_height="#dimen/button_switch_height"
android:background="#drawable/button_switch_back"
/>
In the Activity/Fragment you only have to findViewById and set clickListener to it, and in onClick callback handle it:
switchButton = (SwitchButton) optionsView.findViewById(R.id.ratedGameSwitch);
switchButton.setOnClickListener(this);
#Override
public void onClick(View view) {
if (view.getId() == SwitchButton.BUTTON_ID || view.getId() == SwitchButton.TEXT_ID){
switchButton.toggle(view);
}
}
Try this solution, if you want to create settings activity programmatically.
public class SettingsActivity extends PreferenceActivity {
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PreferenceScreen rootScreen = getPreferenceManager()
.createPreferenceScreen(this);
setPreferenceScreen(rootScreen);
Preference NotifCheck=null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
NotifCheck = new SwitchPreference(this);
} else {
NotifCheck = new CheckBoxPreference(this);
}
NotifCheck.setKey("yourKey");
NotifCheck.setTitle(R.string.ShowNotification);
NotifCheck.setEnabled(true);
rootScreen.addPreference(NotifCheck);
}
}
Related
I have a list of buttons. When I press a button, a View should slide in a downwards motion out of the button, like this:
Start:
Halfway:
End:
How would I go about this? The View that should slide out is bigger than the button, so first hiding the View behind the button and then sliding it downwards causes the View to be visible above the button. That should not happen.
Any ideas or examples on how to approach this?
I believe the simplest approach is to extend Animation class and override applyTransformation() to change the view's height as follows:
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
public class MyCustomAnimation extends Animation {
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private View mView;
private int mEndHeight;
private int mType;
private LinearLayout.LayoutParams mLayoutParams;
public MyCustomAnimation(View view, int duration, int type) {
setDuration(duration);
mView = view;
mEndHeight = mView.getHeight();
mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
mType = type;
if(mType == EXPAND) {
mLayoutParams.height = 0;
} else {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
}
view.setVisibility(View.VISIBLE);
}
public int getHeight(){
return mView.getHeight();
}
public void setHeight(int height){
mEndHeight = height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
if(mType == EXPAND) {
mLayoutParams.height = (int)(mEndHeight * interpolatedTime);
} else {
mLayoutParams.height = (int) (mEndHeight * (1 - interpolatedTime));
}
mView.requestLayout();
} else {
if(mType == EXPAND) {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
mView.requestLayout();
}else{
mView.setVisibility(View.GONE);
}
}
}
}
To use it, set your onclick() as follows:
int height;
#Override
public void onClick(View v) {
if(view2.getVisibility() == View.VISIBLE){
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.COLLAPSE);
height = a.getHeight();
view2.startAnimation(a);
}else{
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.EXPAND);
a.setHeight(height);
view2.startAnimation(a);
}
}
Regards.
Use something like:
Animation a = new ScaleAnimation(1, 1, 0, 1, Animation.RELATIVE_TO_SELF, (float) 0.5, Animation.RELATIVE_TO_SELF, (float) 0);
a.setFillAfter(true);
view.setAnimation(a);
a.setDuration(1000);
view.startAnimation(a);
Here is simple example of hand-made animation, that provide what you want. It works in test app, but I'm not sure that there is no bugs:
public class MainActivity extends Activity implements OnClickListener {
private Timer timer;
private TimerTask animationTask;
private View view1;
private View view2;
boolean animating;
boolean increasing = true;
int initHeight = -1;
private LayoutParams params;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = new Timer();
view1 = findViewById(R.id.view1);// clickable view
view1.setOnClickListener(this);
view2 = findViewById(R.id.view2);// animated view
params = view2.getLayoutParams();
}
#Override
protected void onDestroy() {
super.onDestroy();
timer.cancel();
}
#Override
public void onClick(View v) {
Toast.makeText(this, "start animating...", Toast.LENGTH_SHORT).show();
animationTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (animationFinished()) {
animating = false;
cancel();//canceling animating task
return;
}
params.height += increasing ? 1 : -1;
view2.setLayoutParams(params);
}
});
}
private boolean animationFinished() {
int viewHeight = view2.getHeight();
if (increasing && viewHeight >= initHeight) {
return true;
}
if (!increasing && viewHeight <= 0) {
return true;
}
return false;
}
};
//if we already animating - we just change direction of animation
increasing = !increasing;
if (!animating) {
animating = true;
int height = view2.getHeight();
params.height = height;
view2.setLayoutParams(params);//change param "height" from "wrap_conent" to real height
if (initHeight < 0) {//height of view - we setup it only once
initHeight = height;
}
timer.schedule(animationTask, 0, 10);//changing repeat time here will fasten or slow down animation
}
}
}
Maybe you can set the height to 0 and gradually increase the height. But then you will have the problem that you have to be sure your text is aligned at the bottom of the view. And also to know what the maximal height of the view should be.
use a sliding list adapter so much easier than messing around with animations
https://github.com/tjerkw/Android-SlideExpandableListView
Simply pass android:animateLayoutChanges to LinearLayout that holds all the views, you will achieve your desired result.
I would do it like that. First the layout for the whole collapsible panel component: (pseudo xml)
RelativeLayout (id=panel, clip)
LinearLayout (id=content, alignParentBottom=true)
LinearLayout (id=handle, above=content)
This should ensure that the content is always below the handle.
Then when you need to collapse:
Animate the top margin of content from 0 to -content.height
Animate the height of the panel from current to current-content.height
Is it possible to have a switch widget for ICS versions, but a checkbox for pre ICS? If so, how?
I'm not worried about other components, only switch.
MY SOLUTION
Seeing as switch and checkbox both inherit from CompoundButton, I just did this
((CompoundButton)findViewById(R.id.swTax)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
calculateValues();
}
});
Create a separate layout XML file for ICS versions by placing it in a separate layout folder, e.g. layout-v14. To prevent a lot of duplicate XML, use one main layout file and include the widget. The resulting file structure would look something like this:
layout
mylayout.xml
widget.xml
layout-v14
widget.xml
In mylayout.xml you would have something like:
<include layout="#layout/widget" />
In the Activity for this layout you will have to check the version as well before setting up any interaction with either the CheckBox or Switch widget:
int version = android.os.Build.VERSION.SDK_INT;
if (version >= 14) {
// get your Switch view and set up listeners etc
}
else {
// get your CheckBox view and set up listeners etc
}
I've tried every solution that i found but non of them were fit my needs, so i created my own widget wich is used ObjectAnimator from nineOld compatibility lib and works pretty fine on any android API.
import android.widget.RelativeLayout;
import com.myapp.utilities.AppUtils;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ObjectAnimator;
public class SwitchButton extends RelativeLayout {
public static final int TEXT_SIZE = 11;
public float HANDLE_SHIFT = -40f;
public float TEXT_RIGHT_SHIFT = 40f;
public static int BUTTON_ID = 0x00009999;
public static int TEXT_ID = 0x00008888;
private Button handleButton;
private RoboTextView textView;
private boolean switchEnabled;
private String yesStr;
private String noStr;
private int TEXT_LEFT_PADDING = 13;
private ObjectAnimator animateHandleLeftShift;
private ObjectAnimator animateHandleRightShift;
private int HANDLE_BUTTON_HEIGHT = 22;
private int HANDLE_BUTTON_WIDTH = 42;
private ObjectAnimator animateTextLeftShift;
private ObjectAnimator animateTextRightShift;
public SwitchButton(Context context) {
super(context);
onCreate(context);
}
private void onCreate(Context context) {
float density = context.getResources().getDisplayMetrics().density;
TEXT_LEFT_PADDING *= density;
HANDLE_BUTTON_HEIGHT *= density;
HANDLE_BUTTON_WIDTH *= density;
HANDLE_SHIFT *= density;
TEXT_RIGHT_SHIFT *= density;
yesStr = getContext().getString(R.string.yes).toUpperCase();
noStr = getContext().getString(R.string.no).toUpperCase();
{// Button
handleButton = new Button(getContext());
RelativeLayout.LayoutParams buttonParams = new LayoutParams(HANDLE_BUTTON_WIDTH, HANDLE_BUTTON_HEIGHT);
buttonParams.addRule(RelativeLayout.CENTER_VERTICAL);
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
handleButton.setBackgroundResource(R.drawable.button_switch_handle_selector);
handleButton.setId(BUTTON_ID);
addView(handleButton, buttonParams);
}
{// Text
textView = new RoboTextView(getContext());
LayoutParams textParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
textParams.addRule(RelativeLayout.CENTER_VERTICAL);
textView.setText(yesStr);
textView.setTextColor(getContext().getResources().getColor(R.color.new_normal_gray));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE);
textView.setPadding(TEXT_LEFT_PADDING, 0, 0, 0);
textView.setFont(RoboTextView.ROBOTO_BOLD_FONT);
textView.setId(TEXT_ID);
float shadowRadius = 0.5f ;
float shadowDx = 0;
float shadowDy = 1;
textView.setShadowLayer(shadowRadius, shadowDx, shadowDy, Color.BLACK);
addView(textView, textParams);
}
initFlipAnimation();
}
#Override
public void setOnClickListener(OnClickListener l) {
handleButton.setOnClickListener(l);
textView.setOnClickListener(l);
}
public void toggle(View view){
if (AppUtils.HONEYCOMB_PLUS_API && view.getId() == TEXT_ID) { // ignore text clicks
return;
}
switchEnabled = !switchEnabled;
if (switchEnabled) {
// animate handle to the left
animateHandleLeftShift.start();
animateTextLeftShift.start();
textView.setText(noStr);
} else {
animateHandleRightShift.start();
animateTextRightShift.start();
textView.setText(yesStr);
}
}
private android.view.animation.Interpolator accelerator = new LinearInterpolator();
private static final int DURATION = 70;
private void initFlipAnimation() {
animateHandleLeftShift = ObjectAnimator.ofFloat(handleButton, "translationX", 0f, HANDLE_SHIFT);
animateHandleLeftShift.setDuration(DURATION);
animateHandleLeftShift.setInterpolator(accelerator);
animateHandleRightShift = ObjectAnimator.ofFloat(handleButton, "translationX", HANDLE_SHIFT, 0f);
animateHandleRightShift.setDuration(DURATION);
animateHandleRightShift.setInterpolator(accelerator);
animateHandleLeftShift.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator anim) {
// TODO
}
});
animateTextLeftShift = ObjectAnimator.ofFloat(textView, "translationX", 0f, TEXT_RIGHT_SHIFT);
animateTextLeftShift.setDuration(DURATION);
animateTextLeftShift.setInterpolator(accelerator);
animateTextRightShift = ObjectAnimator.ofFloat(textView, "translationX", TEXT_RIGHT_SHIFT, 0f);
animateTextRightShift.setDuration(DURATION);
animateTextRightShift.setInterpolator(accelerator);
}
}
In XML
<com.chess.SwitchButton
android:id="#+id/ratedGameSwitch"
android:layout_width="#dimen/button_switch_width"
android:layout_height="#dimen/button_switch_height"
android:background="#drawable/button_switch_back"
/>
In the Activity/Fragment you only have to findViewById and set clickListener to it, and in onClick callback handle it:
switchButton = (SwitchButton) optionsView.findViewById(R.id.ratedGameSwitch);
switchButton.setOnClickListener(this);
#Override
public void onClick(View view) {
if (view.getId() == SwitchButton.BUTTON_ID || view.getId() == SwitchButton.TEXT_ID){
switchButton.toggle(view);
}
}
I use a iOS like Segmented control (extension of radio button) with on/off instead of a switch then you can use the same code for old and new SDK.
There is a nice sample project with all the code here:
https://github.com/makeramen/android-segmentedradiobutton
It has both text and graphic samples.
It's happened!
http://developer.android.com/tools/support-library/index.html
Changes for v7 appcompat library:
Added SwitchCompat, a backport of the Switch widget that was added in Android 4.0 (API level 14).
I am trying to implement the spinning activity similar to the the one I have placed below in Android. I believe I should use the ProgressDialog. My issue arises from how to actually manipulate the ProgressDialog to appear like the activity indicator.
Any thoughts are welcome. A link to an example would even be better.
Thanks.
REEDIT:
myProgress.java
public class myProgress extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ProgressDialog d = (ProgressDialog)findViewById(R.id.progres);
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progres"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<ProgressBar
android:id="#+id/progressBar"
android:indeterminate="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
</RelativeLayout>
I wrote my own custom LoadingIndicatorView.
It consists of two files:
LoadingIndicatorBarView
LoadingIndicatorView
Pros:
Programmatically created, no PNG antics meaning scalable and crisp :D
Customizable bar colors and corner radius (if you understand my code)
Cons:
Not as performant as the iOS version (I'm just a beginner Android developer coming from iOS background, what do you expect?) :P
Disclaimer:
Don't blame me if your project blows up, I'm putting this as free public domain code.
You'll notice my coding style and structure resemble my iOS programming codes a lot. I do everything programmatically, no XML if I can get away with it.
How to use this Loading Indicator
After you've copied and pasted all three class source codes into their Java file, you want to use the LoadingIndicatorView class, you shouldn't need to touch the other class, unless you want to customise the colour or rounded corner of each bar.
Create an instance of LoadingIndicatorView like this in your Activity:
import com.companyName.myApplication.views.LoadingIndicatorView;
public class MyActivity extends AppCompatActivity
{
public mainLayout RelativeLayout;
...
public LoadingIndicatorView loadingIndicator;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initViews();
initLayouts();
addViews();
}
public void initViews()
{
mainLayout = new RelativeLayout(this);
mainLayout.setBackgroundColor(Color.BLACK);
...
// ---------------------------------------------------
// 40 here is the radius of the circle
// try and use multiples of 2, e.g. 40, 60, 80 etc
// ---------------------------------------------------
loadingIndicator = new LoadingIndicatorView(this, 40);
// hide until ready to start animating
loadingIndicator.setAlpha(0.0f);
}
public void initLayouts()
{
...
// Need API level 17 for this, set in your AndroidManifeset.xml
mainLayout.setId(View.generateViewId());
loadingIndicator.setId(View.generateViewId());
RelativeLayout.LayoutParams loadingIndicatorLayoutParams = new RelativeLayout.LayoutParams(
(int)(loadingIndicator.radius * 2.0f),
(int)(loadingIndicator.radius * 2.0f)
);
loadingIndicatorLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
loadingIndicator.setLayoutParams(loadingIndicatorLayoutParams);
}
public void addViews()
{
...
mainLayout.addView(loadingIndicator);
setContentView(mainLayout);
}
}
Once you're ready to show it, e.g. in a button click listener, then you call:
loadingIndicator.startAnimating();
When you want to stop and hide the indicator, call:
loadingIndicator.stopAnimating();
You end up with something like this:
LoadingIndicatorView.java
package com.companyName.myApplication.views;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Zhang on 11/02/16.
*/
public class LoadingIndicatorView extends RelativeLayout
{
private Context context;
private int numberOfBars;
public ArrayList<LoadingIndicatorBarView> arrBars;
public float radius;
private boolean isAnimating;
private int currentFrame;
private final Handler handler = new Handler();
private Runnable playFrameRunnable;
public LoadingIndicatorView(Context context, float radius)
{
super(context);
this.context = context;
this.radius = radius;
this.numberOfBars = 12;
initViews();
initLayouts();
addViews();
spreadBars();
}
public void initViews()
{
arrBars = new ArrayList<LoadingIndicatorBarView>();
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = new LoadingIndicatorBarView(context, radius / 10.0f);
arrBars.add(bar);
}
}
public void initLayouts()
{
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
bar.setId(View.generateViewId());
RelativeLayout.LayoutParams barLayoutParams = new RelativeLayout.LayoutParams(
(int)(radius / 5.0f),
(int)(radius / 2.0f)
);
barLayoutParams.addRule(ALIGN_PARENT_TOP);
barLayoutParams.addRule(CENTER_HORIZONTAL);
bar.setLayoutParams(barLayoutParams);
}
}
public void addViews()
{
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
addView(bar);
}
}
public void spreadBars()
{
int degrees = 0;
for(int i = 0; i < arrBars.size(); i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
rotateBar(bar, degrees);
degrees += 30;
}
}
private void rotateBar(LoadingIndicatorBarView bar, float degrees)
{
RotateAnimation animation = new RotateAnimation(0, degrees, radius / 10.0f, radius);
animation.setDuration(0);
animation.setFillAfter(true);
bar.setAnimation(animation);
animation.start();
}
public void startAnimating()
{
setAlpha(1.0f);
isAnimating = true;
playFrameRunnable = new Runnable()
{
#Override
public void run()
{
playFrame();
}
};
// recursive function until isAnimating is false
playFrame();
}
public void stopAnimating()
{
isAnimating = false;
setAlpha(0.0f);
invalidate();
playFrameRunnable = null;
}
private void playFrame()
{
if(isAnimating)
{
resetAllBarAlpha();
updateFrame();
handler.postDelayed(playFrameRunnable, 0);
}
}
private void updateFrame()
{
if (isAnimating)
{
showFrame(currentFrame);
currentFrame += 1;
if (currentFrame > 11)
{
currentFrame = 0;
}
}
}
private void resetAllBarAlpha()
{
LoadingIndicatorBarView bar = null;
for (int i = 0; i < arrBars.size(); i++)
{
bar = arrBars.get(i);
bar.setAlpha(0.5f);
}
}
private void showFrame(int frameNumber)
{
int[] indexes = getFrameIndexesForFrameNumber(frameNumber);
gradientColorBarSets(indexes);
}
private int[] getFrameIndexesForFrameNumber(int frameNumber)
{
if(frameNumber == 0)
{
return indexesFromNumbers(0, 11, 10, 9);
}
else if(frameNumber == 1)
{
return indexesFromNumbers(1, 0, 11, 10);
}
else if(frameNumber == 2)
{
return indexesFromNumbers(2, 1, 0, 11);
}
else if(frameNumber == 3)
{
return indexesFromNumbers(3, 2, 1, 0);
}
else if(frameNumber == 4)
{
return indexesFromNumbers(4, 3, 2, 1);
}
else if(frameNumber == 5)
{
return indexesFromNumbers(5, 4, 3, 2);
}
else if(frameNumber == 6)
{
return indexesFromNumbers(6, 5, 4, 3);
}
else if(frameNumber == 7)
{
return indexesFromNumbers(7, 6, 5, 4);
}
else if(frameNumber == 8)
{
return indexesFromNumbers(8, 7, 6, 5);
}
else if(frameNumber == 9)
{
return indexesFromNumbers(9, 8, 7, 6);
}
else if(frameNumber == 10)
{
return indexesFromNumbers(10, 9, 8, 7);
}
else
{
return indexesFromNumbers(11, 10, 9, 8);
}
}
private int[] indexesFromNumbers(int i1, int i2, int i3, int i4)
{
int[] indexes = {i1, i2, i3, i4};
return indexes;
}
private void gradientColorBarSets(int[] indexes)
{
float alpha = 1.0f;
LoadingIndicatorBarView barView = null;
for(int i = 0; i < indexes.length; i++)
{
int barIndex = indexes[i];
barView = arrBars.get(barIndex);
barView.setAlpha(alpha);
alpha -= 0.125f;
}
invalidate();
}
}
LoadingIndicatorBarView.java
package com.companyName.myApplication.views;
import android.content.Context;
import android.graphics.Color;
import android.widget.RelativeLayout;
import com.companyName.myApplication.helper_classes.ToolBox;
/**
* Created by Zhang on 11/02/16.
*/
public class LoadingIndicatorBarView extends RelativeLayout
{
private Context context;
private float cornerRadius;
public LoadingIndicatorBarView(Context context, float cornerRadius)
{
super(context);
this.context = context;
this.cornerRadius = cornerRadius;
initViews();
}
public void initViews()
{
setBackground(ToolBox.roundedCornerRectWithColor(
Color.argb(255, 255, 255, 255), cornerRadius));
setAlpha(0.5f);
}
public void resetColor()
{
setBackground(ToolBox.roundedCornerRectWithColor(
Color.argb(255, 255, 255, 255), cornerRadius));
setAlpha(0.5f);
}
}
Toolbox.java
package com.companyName.myApplication.helper_classes;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
/**
* Created by Zhang on 3/02/16.
*/
public class ToolBox
{
private static ToolBox instance;
public Context context;
private ToolBox()
{
}
public synchronized static ToolBox getInstance()
{
if(instance == null)
{
instance = new ToolBox();
}
return instance;
}
public static ShapeDrawable roundedCornerRectOutlineWithColor(int color, float cornerRadius,
float strokeWidth)
{
float[] radii = new float[] {
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
shape.getPaint().setStrokeWidth(strokeWidth);
shape.getPaint().setStyle(Paint.Style.STROKE);
return shape;
}
public static ShapeDrawable roundedCornerRectWithColor(int color, float cornerRadius)
{
float[] radii = new float[] {
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
return shape;
}
public static ShapeDrawable roundedCornerRectWithColor(int color, float topLeftRadius, float
topRightRadius, float bottomRightRadius, float bottomLeftRadius)
{
float[] radii = new float[] {
topLeftRadius, topLeftRadius,
topRightRadius, topRightRadius,
bottomRightRadius, bottomRightRadius,
bottomLeftRadius, bottomLeftRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
return shape;
}
public static int getScreenWidth()
{
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
public static int getScreenHeight()
{
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
public static int getScreenOrientation(Context context)
{
return context.getResources().getConfiguration().orientation;
}
public static boolean isLandscapeOrientation(Context context)
{
return getScreenOrientation(context) == Configuration.ORIENTATION_LANDSCAPE;
}
}
This Toolbox class is my convenience helper class to create rounded corner shapes etc in all my projects.
Hope that helps :D
this is how i achieve it
here is the code
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_LOADING:
final Dialog dialog = new Dialog(this, android.R.style.Theme_Translucent);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.loading);
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
//onBackPressed();
}
});
return dialog;
default:
return null;
}
};
here is the loading.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progres"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<ProgressBar
android:indeterminate="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
</RelativeLayout>
call the dialog with
showDialog(DIALOG_LOADING);
hide it using
dismissDialog(DIALOG_LOADING);
UPDATE
if you want and custom indicator you can do the following in the layout.xml.
replace the ProgressBar with an ImageView
set the background of the ImageView to a AnimationDrawable
you can start the animation in onPrepareDialog
You are looking for progressDialog i believe. This link can you set you start with it.
http://www.helloandroid.com/tutorials/using-threads-and-progressdialog
pd = ProgressDialog.show(this, "Working..", "Calculating Pi", true,
false);
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
pd.dismiss();
tv.setText(pi_string);
}
};
Just look at this library. IOSDialog/Spinner library
It is very easy to use and solves your problem. With it, you can easily create and use spinner like in IOS.
The example of code:
final IOSDialog dialog1 = new IOSDialog.Builder(IOSDialogActivity.this)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
dialog0.show();
}
})
.setDimAmount(3)
.setSpinnerColorRes(R.color.colorGreen)
.setMessageColorRes(R.color.colorAccent)
.setTitle(R.string.standard_title)
.setTitleColorRes(R.color.colorPrimary)
.setMessageContent("My message")
.setCancelable(true)
.setMessageContentGravity(Gravity.END)
.build();
Result
final IOSDialog dialog0 = new IOSDialog.Builder(IOSDialogActivity.this)
.setTitle("Default IOS bar")
.setTitleColorRes(R.color.gray)
.build();
Result: stadard IOS Dialog
I have created an animation using the following code.
private AnimationSet rootSet = new AnimationSet(true);
private int xstart=258;
private int ystart=146;
for(; k<points.length; k++) {
if(k==1) {
x1 = headX(xstart);
y1 = headY(ystart);
_animTime = 10;
} else {
x1 = headX(points[k-1][0]);
y1 = headY(points[k-1][1]);
}
translate = new TranslateAnimation((float)x1, (float)x2, (float)y1, (float)y2);
translate.setDuration(_animTime);
translate.setFillAfter(true);
translate.setInterpolator(new AccelerateDecelerateInterpolator());
totalAnimTime += _animTime;
translate.setStartOffset(totalAnimTime);
rootSet.addAnimation(translate);
rootSet.setFillAfter(true);
}
imv1.startAnimation(rootSet);
It is working fine. Now I have to add pause and play feature for this animation. How can I do that?
Since you have extended with more information regarding that you explicity wanted to use AnimationSet, I have found another solution that should work for you.
Sample code:
A class that extends AnimationSet as you will need in order to cancel an AnimationSet:
public class CustomAnimationSet extends AnimationSet {
private AnimationListener mCustomAnimationSetListener;
public CustomAnimationSet(boolean interpolator) {
super(interpolator);
}
public CustomAnimationSet(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setAnimationListener(AnimationListener listener) {
super.setAnimationListener(listener);
mCustomAnimationSetListener = listener;
}
/**
* Your cancel method....
*/
public void cancel() {
// Make sure you're cancelling an ongoing AnimationSet.
if(hasStarted() && !hasEnded()) {
if(mCustomAnimationSetListener != null) {
mCustomAnimationSetListener.onAnimationEnd(this);
}
}
// Reset the AnimationSet's start time.
setStartTime(Float.MIN_VALUE);
}
}
In your Activity class:
private CustomAnimationSet mAnimationSet;
// Init stuff.
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.onPlayButton:
// Might wanna add Animations before starting next time?
mAnimationSet.start();
case R.id.onPauseButton:
mAnimationSet.cancel();
mAnimationSet.reset();
}
}
This is just an example. At the moment I do not have opportunity to test it by myself, this was just written for example purpose.
Is there a way to animate a text color change (from anycolor to white)?
The only variant I came up with, is placing two textviews (with the same text) in one place, and fading the top one, so the bottom one (that has a white color) will become visible.
P.S. I scrapped the variant of the 2 TextViews since it looked weird (edges weren't smooth and, since I have a lot of such elements on the screen it was really lagging the scrolling). What I did, was a crazy hack that does the animation with the use of a Thread and setTextColor (that also forces redraw of a textview).
Since I needed only 2 color changes (from red to white, and from green to white) I hardcoded the values and all of the transition colors between them. So here's how it looks:
public class BlinkingTextView extends TextView {
public BlinkingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void animateBlink(final boolean red) {
if (animator != null) {
animator.drop();
}
animator = new Animator(this, red);
animator.start();
}
public void clearBlinkAnimation() {
if (animator != null) {
animator.drop();
}
}
private Animator animator;
private final static class Animator extends Thread {
public Animator(final TextView textView, final boolean red) {
this.textView = textView;
if (red) {
SET_TO_USE = RED;
} else {
SET_TO_USE = GREEN;
}
}
private TextView textView;
private final int[] SET_TO_USE;
private final static int[] RED = {
-2142396,
-2008754,
-1874854,
-1740697,
-1540490,
-1405563,
-1205099,
-1004634,
-804170,
-669243,
-469036,
-334879,
-200979,
-67337,
-1
};
private final static int[] GREEN = {
-6959821,
-6565826,
-6106293,
-5646758,
-5055894,
-4530309,
-3939444,
-3283042,
-2692177,
-2166592,
-1575728,
-1116193,
-656660,
-262665,
-1
};
private boolean stop;
#Override
public void run() {
int i = 0;
while (i < 15) {
if (stop) break;
final int color = SET_TO_USE[i];
if (stop) break;
textView.post(new Runnable() {
#Override
public void run() {
if (!stop) {
textView.setTextColor(color);
}
}
});
if (stop) break;
i++;
if (stop) break;
try {
Thread.sleep(66);
} catch (InterruptedException e) {}
if (stop) break;
}
}
public void drop() {
stop = true;
}
}
}
You can use new Property Animation Api for color animation:
Integer colorFrom = getResources().getColor(R.color.red);
Integer colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
textView.setTextColor((Integer)animator.getAnimatedValue());
}
});
colorAnimation.start();
For backward compatability with Android 2.x use Nine Old Androids library from Jake Wharton.
The Easiest solution will be to use Object Animators :
ObjectAnimator colorAnim = ObjectAnimator.ofInt(yourTextView, "textColor",
Color.RED, Color.GREEN);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.start();
No need to keep handles to the two text views. First add the fadeIn/fadeOut animations:
textSwitcher.setInAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
textSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
then:
TextView currentTextView = (TextView)(textSwitcher.getNextView().equals(
textSwitcher.getChildAt(0)) ?
textSwitcher.getChildAt(1) : textSwitcher.getChildAt(0)
);
// setCurrentText() first to be the same as newText if you need to
textSwitcher.setTextColor(fadeOutColor);
((TextView) textSwitcher.getNextView()).setTextColor(Color.WHITE);
textSwitcher.setText(newText);
Just implemented it like this so proven to work.
best way use ValueAnimator and ColorUtils.blendARGB
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
valueAnimator.setDuration(325);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fractionAnim = (float) valueAnimator.getAnimatedValue();
textView.setTextColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF")
, Color.parseColor("#000000")
, fractionAnim));
}
});
valueAnimator.start();
Although I haven't found a totally distinct method, I have tried to use a TextSwitcher (with the fade animation) to create the colour-change effect. A TextSwitcher is a kind of ViewSwitcher which literally animates between two (internal) TextViews. Did you manually implement the same system unknowingly? ;) It manages a bit more of the process for you, so you may find it easier to work with (especially if you want to try more involved animations). I would create new subclass of TextSwitcher and some methods e.g. setColour() which can set the new colour and then trigger an animation. The animation code can then be moved outside of your main application.
make sure you keep a handle on the two TextViews that are put into the switcher
change the colour of the other TextView and call setText() to animate between them
If you are already using a ViewSwitcher then I don't think there is an easier way to implement this.
As others mention, using ObjectAnimator solves for this. However, in the existing posts - I wasn't seeing how to set duration. For me the color change would happen immediately.
The solution below shows:
setting the animation with some interval; thanks to post: https://plus.google.com/+CyrilMottier/posts/X4yoNHHszwq
a way to continuously cycle back and forth between the 2 colors
void animateTextViewColors(TextView textView, Integer colorTo) {
final Property<TextView, Integer> property = new Property<TextView, Integer>(int.class, "textColor") {
#Override
public Integer get(TextView object) {
return object.getCurrentTextColor();
}
#Override
public void set(TextView object, Integer value) {
object.setTextColor(value);
}
};
final ObjectAnimator animator = ObjectAnimator.ofInt(textView, property, colorTo);
animator.setDuration(8533L);
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new DecelerateInterpolator(2));
animator.start();
}
void oscillateDemo(final TextView textView) {
final int whiteColor = ContextCompat.getColor(TheApp.getAppContext(), R.color.white);
final int yellowColor = ContextCompat.getColor(TheApp.getAppContext(), R.color.yellow);
final int counter = 100;
Thread oscillateThread = new Thread() {
#Override
public void run() {
for (int i = 0; i < counter; i++) {
final int fadeToColor = (i % 2 == 0)
? yellowColor
: whiteColor;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
animateTextViewColors(textView, fadeToColor);
}
});
try {
Thread.sleep(2450);
}
catch (InterruptedException iEx) {}
}
}
};
oscillateThread.start();
}
I scrapped the variant of the 2 TextViews since it looked weird (edges weren't smooth and, since I have a lot of such elements on the screen it was really lagging the scrolling). What I did, was a crazy hack that does the animation with the use of a Thread and setTextColor (that also forces redraw of a textview).
Since I needed only 2 color changes (from red to white, and from green to white) I hardcoded the values and all of the transition colors between them. So here's how it looks:
public class BlinkingTextView extends TextView {
public BlinkingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void animateBlink(final boolean red) {
if (animator != null) {
animator.drop();
}
animator = new Animator(this, red);
animator.start();
}
public void clearBlinkAnimation() {
if (animator != null) {
animator.drop();
}
}
private Animator animator;
private final static class Animator extends Thread {
public Animator(final TextView textView, final boolean red) {
this.textView = textView;
if (red) {
SET_TO_USE = RED;
} else {
SET_TO_USE = GREEN;
}
}
private TextView textView;
private final int[] SET_TO_USE;
private final static int[] RED = {
-2142396,
-2008754,
-1874854,
-1740697,
-1540490,
-1405563,
-1205099,
-1004634,
-804170,
-669243,
-469036,
-334879,
-200979,
-67337,
-1
};
private final static int[] GREEN = {
-6959821,
-6565826,
-6106293,
-5646758,
-5055894,
-4530309,
-3939444,
-3283042,
-2692177,
-2166592,
-1575728,
-1116193,
-656660,
-262665,
-1
};
private boolean stop;
#Override
public void run() {
int i = 0;
while (i < 15) {
if (stop) break;
final int color = SET_TO_USE[i];
if (stop) break;
textView.post(new Runnable() {
#Override
public void run() {
if (!stop) {
textView.setTextColor(color);
}
}
});
if (stop) break;
i++;
if (stop) break;
try {
Thread.sleep(66);
} catch (InterruptedException e) {}
if (stop) break;
}
}
public void drop() {
stop = true;
}
}
}
The issue I found with valueAnimator as well as ObjectAnimator is that the animator iterates through a number of random colors, and the transition doesn't look smooth. I wrote the following code which worked smoothly. Hope it helps someone else also.
public static void changeTextColor(final TextView textView, int startColor, int endColor,
final long animDuration, final long animUnit){
if (textView == null) return;
final int startRed = Color.red(startColor);
final int startBlue = Color.blue(startColor);
final int startGreen = Color.green(startColor);
final int endRed = Color.red(endColor);
final int endBlue = Color.blue(endColor);
final int endGreen = Color.green(endColor);
new CountDownTimer(animDuration, animUnit){
//animDuration is the time in ms over which to run the animation
//animUnit is the time unit in ms, update color after each animUnit
#Override
public void onTick(long l) {
int red = (int) (endRed + (l * (startRed - endRed) / animDuration));
int blue = (int) (endBlue + (l * (startBlue - endBlue) / animDuration));
int green = (int) (endGreen + (l * (startGreen - endGreen) / animDuration));
textView.setTextColor(Color.rgb(red, green, blue));
}
#Override
public void onFinish() {
textView.setTextColor(Color.rgb(endRed, endGreen, endBlue));
}
}.start();
}