开发者

programmatically darken a View android

I found how to change the opacity of a View, but I need to actually darken a View. My best idea is to put a transparent black rectangle over it and then slowly increase the opacity of the rectangle.

Do you know a nicer way to do it?

public class Page07AnimationView extends ParentPageAnimationView {
    private final String TAG = this.getClass().getSimpleName();
    private ImageView overlay;
    private int mAlpha = 0;

    public Page07AnimationView(Context context) {
        super(context);
    }

    public Page07AnimationView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void init()
    {
        overlay = new ImageView(mContext);
        overlay.setImageResource(R.drawable.black_background);
        overlay.setAlpha(0);
        overlay.setWillNotDraw(false);
        // make the PageAniSurfaceView focusable so it can handle events
        setFocusable(true);
    }

    protected void draw_bitmaps(Canvas canvas)
    {
        overlay.draw(canvas);
        update_bitmaps();
        invalidate();
    }

    public void update_bitmaps()
    {
        if(mAlpha < 250)
        {
            mAlpha += 10;
            overlay.setAlpha(mAlpha);
        }
    }
}

The code above isn't doing what I had hoped. Page07AnimationView is added to a FrameLayout over the view I need to darken. R.drawable.black_background points to a 787px x 492px black png image.

I added overlay.setWillNotDraw(false); but it didn't help. I changed the first setAlpha(0) to setAlpha(255) but that didn't help. I removed the setAlpha() calls altogether, but it didn't help.

This basic technique of adding a PageNNAnimationView has been working to draw Bitmaps, but not to draw ImageView overlay. (I would use Bitmaps, but they don't seem to have an alpha component.)

Edit2: this is the parent of the class above:

public class ParentPageAnimationView extends View {
    pr开发者_如何转开发ivate final String TAG = this.getClass().getSimpleName();
    protected Context mContext;

    public ParentPageAnimationView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public ParentPageAnimationView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    protected void init()
    {
    }

    protected void draw_bitmaps(Canvas canvas)
    {
        // will be overridden by child classes
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(this.getVisibility() == View.VISIBLE)
        {
            if(canvas != null)
            {
                draw_bitmaps(canvas);
            }
        }
    }

    public void update_bitmaps() 
    {
        // will be overridden by child classes
    }

    public void elementStarted(PageElement _pageElement) {
        // Nothing in parent class
    }

    public void elementFinished(PageElement mElement) {
        // Nothing in parent class
    }
}


In case of an ImageView, here's one way to achieve it:

imageView.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);


I would rather do it in the opposite way - put a dark rectangle behind the view and set the view's opacity. This saves painting the rectangle when the view is 100% opaque.


I would do something like this:

view.getBackground().setColorFilter(color, PorterDuff.Mode.DARKEN);

Use black color with some alpha like 0x7f000000 for a typical darkening.

It's more concise and you can also darken the View with animation or scrolling event for example. Just set Color.argb(alpha, 0, 0, 0) as the color and animate alpha, or change it based on the scrolling offset.


This is how I ended up doing it. The key was to use a Paint with its alpha set to whatever I wanted.

public class Page07AnimationView extends ParentPageAnimationView {
    private final String TAG = this.getClass().getSimpleName();
    private Bitmap bitmap;
    private BitmapDrawable drawable;
    private ImageView overlay;
    private int which = -1;
    private long last_time;
    private Page07State state;
    private int mAlpha;
    private int maxAlpha;
    private Paint mPaint;
    private int _alpha_step;
    private int minAlpha;

    public enum Page07State {
        WAITING, DARKENING, DARKENED
    }

    public Page07AnimationView(Context context) {
        super(context);
    }

    public Page07AnimationView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void init()
    {
        minAlpha = 0;
        mAlpha = minAlpha;
        _alpha_step = 5;
        maxAlpha = 255;
        mPaint = new Paint();
        mPaint.setAlpha(minAlpha);
        state = Page07State.WAITING;
        overlay = new ImageView(mContext);
        overlay.setImageResource(R.drawable.black_background);
        drawable = (BitmapDrawable) overlay.getDrawable();
        bitmap = drawable.getBitmap();
        last_time = 0;
    }

    protected void draw_bitmaps(Canvas canvas)
    {
        if(state != Page07State.WAITING)
        {
            DebugLog.d(TAG, "drawing " + Integer.toString(which));
            canvas.drawBitmap(bitmap, 0, 0, mPaint);
        }
        update_bitmaps();
        invalidate();
    }

    public void update_bitmaps()
    {
        if(state == Page07State.DARKENING)
        {
            if(mAlpha < maxAlpha)
            {
                if(System.currentTimeMillis() > last_time + 12)
                {
                    last_time = System.currentTimeMillis();
                    mAlpha += _alpha_step;
                    mPaint.setAlpha(mAlpha);
                }
            }
            else
            {
                state = Page07State.DARKENED;
            }
        }
    }

    public void runAnimation()
    {
        state = Page07State.DARKENING;
    }
}


Adding to android developer's answer:

imageView.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);

you can setColorFilter on any view like this:

GradientDrawable gd = (GradientDrawable) textView.getBackground();
gd.setColor(color); //you can also set BG color to a textview like this
gd.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);


you could try using the Alpha animation like this (perhaps on the rectangle):

    Animation animation = new AlphaAnimation(0.0f, 1.0f);
    animation.setDuration(350);

That would cause the rectangle to gradually become opaque over 350 seconds...


Android actually exposes a drawable which can be used to darken views. You can easily attach it to any view with an Overlay.

Here are two extension functions which can be used to darken any view.

fun View.darken() {
    val darkOverlay = ResourcesCompat.getDrawable(
            resources,
            android.R.drawable.screen_background_dark_transparent,
            context.theme
    )!!.mutate() // We mutate the drawable so we can later implement a fade in/out animation and animate the Drawable's alpha property. Since Drawables share their state we need to mutate otherwise we would impact all instances of this drawable
    darkOverlay.setBounds(0, 0, width, height)
    setTag(R.id.dark_overlay, darkOverlay)
    overlay.add(darkOverlay)
}

fun View.lighten() {
    (getTag(R.id.dark_overlay) as? Drawable)?.let {
        overlay.remove(it)
        setTag(R.id.dark_overlay, null)
    }
}

Make sure you add the id to ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="dark_overlay" type="id" />
</resources>

And if you're darkening your application's root layout and would like to darken the NavigationBar as well, you might need to add the the following to your theme in styles.xml

<style name="BaseTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <!-- required for api 29 otherwise the system will set a white background color to the NavigationBar to ensure the buttons are visible -->
    <item name="android:enforceNavigationBarContrast">false</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>


You should check iPaulPro's answer in this question. You will need to extend ImageView and override the onDraw() method.

Depending on what you are going to do, Alexandru Cristescu's answer is also valid but you should call setFillAter(true) for the animation to persist after finished.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜