hardware accelerated webview slide-in animation flickering on ICS - java

Our application makes heavy use of webviews. When testing on ICS, we noticed that we need to set the application property hardwareAccelerated="true". However, doing this caused a few other bugs in our application, most of which we have fixed. We are still having problems getting our 'slide-in' animation working. Without any code changes, the animation simply does a 'reveal' type animation rather than sliding. We have tries a few different things:
Adding a small alpha transition (.99->1). This changes the 'reveal' to a 'slide' but causes some weird artifacts on the screen sometimes.
using hardware layers during the animation. This doesn't work consistently.
using software layers during the animation. This works, but causes a slow redraw after the slide-in animation completes for the first time.
Approach 3 is the most promising, but we have not figured out how to avoid the redraw. We have created a small test case using a sample project with a single activity:
activity class:
int accelValue = View.LAYER_TYPE_NONE; //hack
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addClickListenerAnimation(R.id.buttonhl, R.anim.to_left, View.VISIBLE, View.INVISIBLE);
addClickListenerAnimation(R.id.buttonhr, R.anim.to_right, View.VISIBLE, View.INVISIBLE);
addClickListenerAnimation(R.id.buttonsl, R.anim.from_left, View.INVISIBLE, View.VISIBLE);
addClickListenerAnimation(R.id.buttonsr, R.anim.from_right, View.INVISIBLE, View.VISIBLE);
final Button b = (Button) findViewById(R.id.accel);
b.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
accelValue = ++accelValue % 3;
b.setText(accelValue == 0 ? "NONE" : accelValue == 1 ? "S/W" : "H/W");
}
});
}
private void addClickListenerAnimation(int buttonId, final int animId, final int startVis, final int endVis)
{
Button b = (Button) findViewById(buttonId);
b.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
final WebView wv = (WebView) findViewById(R.id.webview);
final View layout = findViewById(R.id.frame);
Animation a = AnimationUtils.loadAnimation(getApplicationContext(), animId);
a.setDuration(500);
a.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
Log.i("sb", "layer type was " + wv.getLayerType());
Log.i("sb", "llayout layer type was " + layout.getLayerType());
wv.setLayerType(accelValue, null);
Log.i("sb", "setting layer type " + accelValue);
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
wv.setLayerType(View.LAYER_TYPE_NONE, null);
Log.i("sb", "restoring layout layer type " + layout.getLayerType());
}
});
layout.setAnimation(a);
layout.setVisibility(endVis);
}
});
}
#Override
protected void onStart()
{
super.onStart();
((WebView)findViewById(R.id.webview)).loadUrl("http://www.wikipedia.org");
}
from_left.xml animation (other animation xmls are similar):
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true">
<translate android:fromXDelta="-100%"
android:toXDelta="0%"
android:fromYDelta="0%"
android:toYDelta="0%"
android:duration="600"
android:zAdjustment="bottom"
/>
</set>
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/llayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/frame"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="invisible"
>
<WebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</FrameLayout>
<View
android:layout_width="fill_parent"
android:layout_height="80dp"
android:background="#FF000000" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="80dp"
android:orientation="horizontal" >
<Button
android:id="#+id/buttonsl"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="show L" />
<Button
android:id="#+id/buttonhl"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="hide L" />
<Button
android:id="#+id/buttonsr"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="show R" />
<Button
android:id="#+id/buttonhr"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="hide R" />
<Button
android:id="#+id/accel"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="NONE" />
</LinearLayout>
</LinearLayout>
manifest:
<uses-sdk android:minSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET" />

Well, if you are trying to support ICS and onward, you can use the new Animation APIs as they are much easier to use and I believe will be very smooth.
Here are two links to take a look into this API:
this and this
And if you want to use this API for older versions, try NineOldAndroids
Edit: Try setting the WebView's Layer to a Software one:
web.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
It stopped the flickering but you lose hardware acceleration inside the view. I am not sure but I guess that for stuff like animation the view would still be considered as having hardware acceleration since its "container" still does. But I could be completely wrong too.

it worked fine for me using
web.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
the flickering issue was solved

From you description it seems like that's the same problem I had, though I'm not completely sure of that. At first I used your solution, but now I have one that works even better. I simply set
android:alwaysDrawnWithCache="true"
on the web view. That get's rid of all flickering and wrong drawing.
I hope it helps!

Related

ViewHolder views flash/blink OnBind

Issue
Every time I call the OnBind() method inside my holders, views within them will flash or blink or maybe even fade.
I'm just pretty lost as to why, I've tried a number of things.
Changing them to have null as a background or have them all be a solid color.
Making sure nothing is gone at any point.
Removed any animations.
Asked Google a thousand times, a thousand ways.
Searched on here.
Thoughts
I've always thought that I would have to eventually try and hide it with animations and styling. Seeing it as just a side effect of the binding.
This is part of a larger project, obviously. So I still have to do all the animations, that's why I haven't done it for this yet. Figured I'd just ask and see if anyone could help find the solution another way in the meantime.
Any help, tips or advice would be great. Hopefully the code links work and are build able for everyone.
Thanks,
Jon.
Code
I'm not sure how much code I need to add in here, so I'll go small at first. If more should be added please let me know and I will. However I put it all on Github and Dropbox(Example Apk & Zip).
Links are at the bottom.
HeaderHolder.java
public class HeaderHolder extends BaseHolder {
#Bind(R.id.header_title_text)
TextView _titleTextView;
#Bind(R.id.header_status_image)
ImageView _statusImageView;
#BindDrawable(R.drawable.ic_selected)
Drawable _statusSelected;
#BindDrawable(R.drawable.ic_non_selected)
Drawable _statusNonselected;
private Header _header;
public HeaderHolder(View root, HolderCallBacks callbacks) {
super(null, root, callbacks);
}
#Override
public void OnBind(Base model) {
this._header = (Header) model;
String n = model._name();
this._titleTextView.setText(n);
this._statusImageView.setImageDrawable(this._header._iconset()._selected()
? this._statusSelected : this._statusNonselected);
}
#OnClick(R.id.header_item_wrapper)
public void _headerClick(View view) {
this._callbacks.OnHolderClick(view, this._header);
}
}
IconsetHolder.java
public class IconsetHolder extends BaseHolder {
#Bind(R.id.iconset_icon_recycler)
RecyclerView _iconsRecycler;
private AdapterCallBacks _adapterCallbacks;
public IconsetHolder(Context context, View root, AdapterCallBacks callbacks) {
super(context, root, null);
this._adapterCallbacks = callbacks;
}
#Override
public void OnBind(Base model) {
Iconset i = (Iconset) model;
this._iconsRecycler.setLayoutManager(new GridLayoutManager(
this._context, i._span(), GridLayoutManager.HORIZONTAL, false));
this._iconsRecycler.setAdapter(new ModelsAdapter(i._icons(), this._adapterCallbacks));
}
}
item_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/header_item_wrapper"
android:layout_width="wrap_content"
android:layout_height="56dip"
android:background="#595959"
tools:context=".views.adapters.holders.HeaderHolder">
<TextView
android:id="#+id/header_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:textColor="#fff8f8f8"
android:gravity="center_vertical"
android:text="ICONSET"
android:textSize="24sp"
android:layout_centerVertical="true" />
<ImageView
android:id="#+id/header_status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="12dp"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
</RelativeLayout>
item_iconset.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/iconset_item_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#null"
tools:context=".views.adapters.holders.IconsetHolder">
<android.support.v7.widget.RecyclerView
android:id="#+id/iconset_icon_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:background="#595959"
android:layout_marginBottom="16dp"
/>
</RelativeLayout>
Example Links Removed
RecyclerView has some built in animations to it, using the DefaultItemAnimator. Specifically when you call notifiyItemChanged() it does a fade animation for the changing of the data in the ViewHolder. If you would like to disable this you can use the following:
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator(); // your recycler view here
if (animator instanceof DefaultItemAnimator) {
((DefaultItemAnimator) animator).setSupportsChangeAnimations(false);
}
This will disable the item changed animation (the fade you are seeing).

How to use Onchanged and OnTouch for the same boutton in Android

I'm working on an Android application at my work and I am having a problem.
I need to know how I can use the Onchanged and OnTouch methods for the same button with conditions?
For example: If my button is on left than you can only touch it to do something and if it is on right than you can use it as a spinner.
I will suggest to use an image Button for both the case. As your requirement, use image Button as a normal Button when in left-side and spinner when on right. You can make use of a variable "i" to be set if it is in right side. Change the method if it is reset.
<ImageButton
android:id="#+id/imageButtonSpinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
/>
Then define your spinner in terms of what you need inside it. Like this may be....
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/spinnerLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageButton
android:id="#+id/love"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="#+id/hem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="#+id/love"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
In the mainActivity
protected void onCreate(Bundle savedInstanceState) {
...
addListenerToSpinnerButton();
}
private void addListenerToSpinnerButton(){
spinnerButton = (ImageButton) findViewById(R.id.imageButtonSpinner);
spinnerButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if(p != null)
showSpinner(MainActivity.this, p);
}
});
}

WebView in background is still clickable when ProgressBar is shown

I have my android webView app working nice. It also shows the ProgressBar while loading the url and hides it when finish the load with a beautiful effect cause I put some alpha in the ProgressBar layout to see webView in the background. I followed this example..
My problem is: webView is still clickable.
MyManifest.xml: i have all permissions needed I think...
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
main_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/mainLayout"
tools:context="com.siconet.axa.MainActivity" >
<RelativeLayout
android:id="#+id/loadingPanel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" >
<ProgressBar
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:indeterminate="true" />
</RelativeLayout>
<WebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Then I show/hide progressbar in the client and I need to avoid webView from being clicked:
webViewClient.java
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
loadingFinished = false;
// this works like charm
activity.findViewById(R.id.loadingPanel).setVisibility(View.VISIBLE);
activity.findViewById(R.id.webView).setAlpha(0.3f);
// this is not working :(
activity.findViewById(R.id.loadingPanel).setClickable(true);
activity.findViewById(R.id.webView).setClickable(false);
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url)
{
// control of the loading options....
if (loadingFinished && !redirect) {
activity.findViewById(R.id.loadingPanel).setVisibility(View.GONE);
activity.findViewById(R.id.webView).setAlpha(1f);
// not working again :(
activity.findViewById(R.id.loadingPanel).setClickable(false);
activity.findViewById(R.id.webView).setClickable(true);
}
}
I also tried with requestFocus():
activity.findViewById(R.id.loadingPanel).requestFocus();
And adding listeners to the views:
mWebView.setOnTouchListener(new OnTouchListener() { // declare listener...
Any idea of why is this happening???
Add this attribute to your ProgressBar element in XML:
android:clickable="true"
Change your RelativeLayout (the ProgressBar View parent) attributes to:
<RelativeLayout
android:id="#+id/loadingPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:layout_centerInParent="true" >
This will make your RelativeLayout to cover your whole layout (not just 100dpx100dp) and to receive the clicks while its visible.

Difficulties fading out and hiding Relative Layout

I'm trying to do a simple fade-out animation for my "custom" media controller.
The problem I'm having is that the layout does not fade-out, it just disappears after a second (as if I would set the Visibility to GONE with a 1 second delay).
This is my function:
private void fadeOut(final View view) {
final AlphaAnimation fadeOutAnimation = new AlphaAnimation(1.0F, 0.0F);
fadeOutAnimation.setDuration(1000);
fadeOutAnimation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
view.setVisibility(View.GONE);
}
});
view.startAnimation(fadeOutAnimation);
}
And the layout that I'm trying to hide:
<RelativeLayout
android:id="#+id/videoControllerContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="0dp"
android:alpha="50"
android:background="#color/DarkGrey" >
<ImageButton
android:id="#+id/playVideoButton"
android:layout_width="wrap_content"
android:layout_height="50dip"
android:layout_alignParentLeft="true"
android:background="#null"
android:contentDescription="#string/playButton"
android:scaleType="fitCenter"
android:src="#drawable/play" />
<ImageButton
android:id="#+id/fullScreenVideoButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="20dp"
android:background="#null"
android:contentDescription="#string/fullscreen"
android:src="#drawable/fullscreen" />
</RelativeLayout>
It's worth mentioning that all this is being used inside a fragment.
I'm hoping that someone with a bit more experience will take the time and let me know what I'm doing wrong.
I think you should have this in an anim xml file where you would want this:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:interpolator="#android:anim/accelerate_interpolator"
android:duration="1000"/>
</set>
I think your problem is that the interpolator isn't defined. So try to put this line's equivalent in your code:
android:interpolator="#android:anim/accelerate_interpolator"
Hope it helps
EDIT: did a little research, it looks like you want to say
fadeOutAnimation.setInterpolator(new AccelerateInterpolator());

Fade effect between layouts

As by object, I would reproduce fade effect between two layout.
Now I've this situation:
LinearLayout l;
LinearLayout l2;
To switch between them I've used
l.setVisibility(View.GONE);
l2.setVisibility(View.VISIBLE);
I want add fade effect between this transiction, how I can do it?
Here is a working solution to cross fade between 2 layouts:
public class CrossFadeActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crossfade);
final View l1 = findViewById(R.id.l1);
final View l2 = findViewById(R.id.l2);
final Animation fadeOut = AnimationUtils.loadAnimation(CrossFadeActivity.this, R.anim.fade_out);
final Animation fadeIn = AnimationUtils.loadAnimation(CrossFadeActivity.this, R.anim.fade_in);
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fadeOut.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
l1.setVisibility(View.GONE);
}
});
l1.startAnimation(fadeOut);
l2.setVisibility(View.VISIBLE);
l2.startAnimation(fadeIn);
}
});
findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fadeOut.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
l2.setVisibility(View.GONE);
}
});
l2.startAnimation(fadeOut);
l1.setVisibility(View.VISIBLE);
l1.startAnimation(fadeIn);
}
});
}
}
crossfade.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/l1"
android:layout_width="fill_parent"
android:layout_height="300dip"
android:orientation="vertical"
android:background="#drawable/someimage"
>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
<RelativeLayout
android:id="#+id/l2"
android:layout_width="fill_parent"
android:layout_height="300dip"
android:orientation="vertical"
android:background="#drawable/someimage2"
android:visibility="gone"
>
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:layout_centerInParent="true"
/>
</RelativeLayout>
</RelativeLayout>
Where l1 and l2 are 2 random example layouts. The trick is to put them in XML such that they overlap each other (e.g. in a RelativeLayout) with visible / gone, add listeners to the animations to toggle the visibility on finish, and set the view which has to fade in to visible before the animation starts, otherwise the animation will not be visible.
I put the buttons with the listeners to toggle the animation in the layouts itself, because I need to implement it that way but the click listener can be of course somewhere else (if it's only one it should be used in combination with some flag or check to know how to toggle).
These are the animation files. They have to be stored in folder res/anim:
fade_in.xml
<?xml version="1.0" encoding="UTF-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fillAfter="true"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
fade_out.xml
<?xml version="1.0" encoding="UTF-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fillAfter="true"
android:fromAlpha="1.0"
android:toAlpha="0" />
UPDATE:
Instead of using R.anim.fade_in, you can use the default fade_in from Android API (android.R.fade_in):
final Animation fadeIn = AnimationUtils.loadAnimation(CrossFadeActivity.this, android.R.anim.fade_in);
Using android.R.anim.fade_in, you will not need to create the file res/anim/fade_in.xml.
Android has a package with some useful animations on android.R.anim: http://developer.android.com/reference/android/R.anim.html
Using R.anim.fade_out & .R.anim.fade_in you can create an animation which does this. I don't know much about this myself but heres a tutorial regarding animations in android: Animation Tutorial
P.S. This tutorial is not mine thus credit does not go out to me.
Edit:
AnimationSet set = new AnimationSet(true);
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(50);
set.addAnimation(animation);
animation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, -1.0f,Animation.RELATIVE_TO_SELF, 0.0f
);
animation.setDuration(100);
set.addAnimation(animation);
LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f);
l.setLayoutAnimation(controller);
Fade out Animation
public static Animation runFadeOutAnimationOn(Activity ctx, View target) {
Animation animation = AnimationUtils.loadAnimation(ctx,android.R.anim.fade_out);
target.startAnimation(animation);
return animation;
}
I'm guessing you can try something like this, I copy pasted the animation from the tutorial I don't know what it does exactly as I have no experience with Android development. Another example could be Example 2
Since android 3.0 you can use
android:animateLayoutChanges="true"
in a layout in xml and any changes to this specific layouts content at run time will be animated. For example, in your case, you will need to set this attribute for the parent for the parent layout of l and l2 e.g
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="me.kalem.android.RegistrationActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:padding="20dp">
<LinearLayout
android:id="#+id/l1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="visible">
........
</LinearLayout>
<LinearLayout
android:id="#+id/l2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone">
........
</LinearLayout>
</LinearLayout>
Now hiding l1 and showing l2 at runtime will be animated.

Categories