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).
Related
I just want to make the background image to look blur(like defocus),I used alpha but it was not only setting alpha to my background image but also to the whole content...Is there any way that I can set blur effect only to my background image!!!..
need help thanks in advance!!..
Please use below tutorial for blur background
NavigationDrawer :
https://github.com/charbgr/BlurNavigationDrawer
Fragment:
https://github.com/tvbarthel/BlurDialogFragment
Image : If you want to blur an image in layout :
https://github.com/kikoso/android-stackblur
Layout:
https://github.com/PomepuyN/BlurEffectForAndroidDesign
public class MainActivity extends Activity {
private static final String BLURRED_IMG_PATH = "blurred_image.png";
private static final int TOP_HEIGHT = 700;
private ListView mList;
private ImageView mBlurredImage;
private View headerView;
private ImageView mNormalImage;
private ScrollableImageView mBlurredImageHeader;
private Switch mSwitch;
private float alpha;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_main);
// Get the screen width
final int screenWidth = ImageUtils.getScreenWidth(this);
// Find the view
mBlurredImage = (ImageView) findViewById(R.id.blurred_image);
mNormalImage = (ImageView) findViewById(R.id.normal_image);
mBlurredImageHeader = (ScrollableImageView) findViewById(R.id.blurred_image_header);
mSwitch = (Switch) findViewById(R.id.background_switch);
mList = (ListView) findViewById(R.id.list);
// prepare the header ScrollableImageView
mBlurredImageHeader.setScreenWidth(screenWidth);
// Action for the switch
mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mBlurredImage.setAlpha(alpha);
} else {
mBlurredImage.setAlpha(0f);
}
}
});
// Try to find the blurred image
final File blurredImage = new File(getFilesDir() + BLURRED_IMG_PATH);
if (!blurredImage.exists()) {
// launch the progressbar in ActionBar
setProgressBarIndeterminateVisibility(true);
new Thread(new Runnable() {
#Override
public void run() {
// No image found => let's generate it!
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
Bitmap newImg = Blur.fastblur(MainActivity.this, image, 12);
ImageUtils.storeImage(newImg, blurredImage);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateView(screenWidth);
// And finally stop the progressbar
setProgressBarIndeterminateVisibility(false);
}
});
}
}).start();
} else {
// The image has been found. Let's update the view
updateView(screenWidth);
}
String[] strings = getResources().getStringArray(R.array.list_content);
// Prepare the header view for our list
headerView = new View(this);
headerView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, TOP_HEIGHT));
mList.addHeaderView(headerView);
mList.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item, strings));
mList.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
/**
* Listen to the list scroll. This is where magic happens ;)
*/
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Calculate the ratio between the scroll amount and the list
// header weight to determinate the top picture alpha
alpha = (float) -headerView.getTop() / (float) TOP_HEIGHT;
// Apply a ceil
if (alpha > 1) {
alpha = 1;
}
// Apply on the ImageView if needed
if (mSwitch.isChecked()) {
mBlurredImage.setAlpha(alpha);
}
// Parallax effect : we apply half the scroll amount to our
// three views
mBlurredImage.setTop(headerView.getTop() / 2);
mNormalImage.setTop(headerView.getTop() / 2);
mBlurredImageHeader.handleScroll(headerView.getTop() / 2);
}
});
}
private void updateView(final int screenWidth) {
Bitmap bmpBlurred = BitmapFactory.decodeFile(getFilesDir() + BLURRED_IMG_PATH);
bmpBlurred = Bitmap.createScaledBitmap(bmpBlurred, screenWidth, (int) (bmpBlurred.getHeight()
* ((float) screenWidth) / (float) bmpBlurred.getWidth()), false);
mBlurredImage.setImageBitmap(bmpBlurred);
mBlurredImageHeader.setoriginalImage(bmpBlurred);
}
}
Kotlin code, use view effect Library :
1- Add library in build.gradle:
implementation 'com.github.mirrajabi:view-effects:e355a1bac4'
2- Blure the background of root view or view, here Constraint Layout blured by 20%
ViewFilter.getInstance(this)
.setRenderer( BlurRenderer(20))
.applyFilterOnView( root_constraintLayout,
root_constraintLayout )
my github repository for blur background : link
I want to animate a rectangle filled with one opaque color. The attributes I will be animating is the translation, and the width of the active menu item.
I know how to animate things, but in this case, I want it to do no layouts on the view, since my animation will occur inside a LinearLayout, and it will not exceed it's size.
The Blue line on top of my layout is what I will be animating. It will go horizontally to the left and right, while changing it's width, so that it fits on the selected menu item.
I usually work with animations on the margin, but it consumes a lot of processing to re-calculate bounds on the layout process.
Any suggestions on how to do that?
That depends entirely on what API level you're targetting, if you're only targeting >3.0 then ObjectAnimator and ValueAnimator or the nicer ViewPropertyAnimator are your best friend, they let you do simple things like "move the X value of this 100dp while increasing the width by a factor of two, in 300ms".
If you're targeting a lower API level check out NineOldAndroids which brings that functionality over to all versions of Android.
To do what you want to do it'd be something along the lines of:
myImageView.scaleXBy(FACTOR_NEEDED_FOR_NEW_WIDTH);
and that's all to it.
As a sidenote: It looks like you might be attempting to replicate a ViewPager's indicator, in which case you should be using an actual indicator.
I had to animate the Margin and the Width of the view, because there was no way out, since I'm using android version >=8.
Here is my two classes that can do this:
MarginAnimation class:
public class MarginAnimation extends Animation{// implements AnimationListener{
public static String TAG = "MarginAnimation";
protected View animatingView;
protected int fromMarginLeft = 0;
protected int fromMarginTop = 0;
protected int toMarginLeft = 0;
protected int toMarginTop = 0;
protected LayoutParams layoutParam;
public MarginAnimation(View v, int toMarginLeft, int toMarginTop) {
this.toMarginLeft = toMarginLeft;
this.toMarginTop = toMarginTop;
this.animatingView = v;
// Save layout param
layoutParam = (LayoutParams) animatingView.getLayoutParams();
// Save current margins as initial state
saveCurrent();
// Set the listner to be self object
// setAnimationListener(this);
}
public MarginAnimation(View v, int fromMarginLeft, int toMarginLeft, int fromMarginTop, int toMarginTop) {
this.fromMarginLeft = fromMarginLeft;
this.toMarginLeft = toMarginLeft;
this.fromMarginTop = fromMarginTop;
this.toMarginTop = toMarginTop;
this.animatingView = v;
// Save layout param
layoutParam = (LayoutParams) animatingView.getLayoutParams();
// Set the listner to be self object
// setAnimationListener(this);
}
protected void saveCurrent(){
fromMarginLeft = layoutParam.leftMargin;
fromMarginTop = layoutParam.topMargin;
}
long lastTime = 0;
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// long thisTime = System.nanoTime();
// if(lastTime != 0)
// Log.e(TAG, ((thisTime - lastTime) / 1000) + "delta Anim.");
// lastTime = thisTime;
layoutParam.leftMargin = (int)(fromMarginLeft + (toMarginLeft - fromMarginLeft) * interpolatedTime);
layoutParam.topMargin = (int)(fromMarginTop + (toMarginTop- fromMarginTop) * interpolatedTime);
animatingView.setLayoutParams(layoutParam);
}
#Override
public boolean willChangeBounds() {
return false;
}
}
MarginAndWidthAnimation class:
public class MarginAndWidthAnimation extends MarginAnimation{
public static String TAG = "MarginAndWidthAnimation";
int toWidth;
int fromWidth;
public MarginAndWidthAnimation(View v, int toMarginLeft, int toMarginTop, int toWidth) {
super(v, toMarginLeft,toMarginTop);
this.toWidth = toWidth;
// Log.i(TAG, "++F: "+this.fromWidth+" T: "+this.toWidth);
}
protected void saveCurrent(){
super.saveCurrent();
// fromWidth = animatingView.getWidth();
fromWidth = layoutParam.width;
// Log.i(TAG, "F: "+fromWidth+" T: "+toWidth);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
layoutParam.width = (int)(fromWidth + (toWidth - fromWidth) * interpolatedTime);
// Log.i(TAG, "F: "+fromWidth+" T: "+toWidth+" S: "+layoutParam.width);
super.applyTransformation(interpolatedTime, t);
}
}
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 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);
}
}
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();
}