How do I grey out a view uniformly which contains many different items - ImageViews, TextViews, background image. Do I have to grey out each thing individually? Or is there
Unless somebody else has a better answer, my current method is greying out each item separatedly.
PorterDuffColorFilter greyFilter = new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
myLayout.getBackground().setColorFilter(greyFilter);
myImageView.setColorFilter(greyFilter);
myTextView.setTextColor(0xff777777);
For more or nested children probably a loop with instanceof would be suitable but I don't need it.
Edit: This filter isn't actually grey, a better filter is here: Drawable => grayscale Which can be used in the same way.
It depends entirely on the method you are using to "gray out" items. If you are doing so by calling setEnabled(false)
on the parent ViewGroup
, by default state flags (like disabled) do not trickle down to the child views. However, there are two simple ways you can customize this:
One option is to add the attribute android:duplicateParentState="true"
to each child view in your XML. This will tell the children to get their state flags from the parent. This will mirror ALL flags however, including pressed, checked, etc...not just enabled.
Another option is to create a custom subclass of your ViewGroup
and override setEnabled()
to call all the child views as well, i.e.
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
for(int i=0; i < getChildCount(); i++) {
getChildAt(i).setEnabled(enabled);
}
}
What I like to do is create a view which overlaps the view you are trying to tint and set its background to a transparent color. Then you can turn the tint on and off by setting that views visibility
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewYouWantToTint
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:id="@+id/disabled_tint_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/darkTint"
android:visibility="invisible"/>
</FrameLayout>
Then in your controller:
m_DisabledBlackTintOverlay = (View) view.findViewById(R.id.login_disabled_black_tint_overlay);
m_DisabledBlackTintOverlay.setVisibility(View.VISIBLE);
It's easy to do that by defining a custom view group as follows:
public class MyViewContainer extends XXXLayout {
//XXLayout could be LinearLayout, RelativeLayout or others
private Paint m_paint;
//define constructors here and call _Init() at the end of constructor function
private void
_Init()
{
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
m_paint = new Paint();
m_paint.setColorFilter(new ColorMatrixColorFilter(cm));
}
@Override protected void
dispatchDraw(Canvas canvas)
{
canvas.saveLayer(null, m_paint, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
canvas.restore();
}
}
All children views of MyViewContainer will be shown in grey. :-)
Sam Lu's answer is a good start, but I had performance problems and decided to switch to hardware layers. With hardware layers, you can do it like this:
private final Paint grayscalePaint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
grayscalePaint.setColorFilter(new ColorMatrixColorFilter(cm));
public void setGrayedOut(boolean grayedOut) {
if (grayedOut) {
setLayerType(View.LAYER_TYPE_HARDWARE, grayscalePaint);
} else {
setLayerType(View.LAYER_TYPE_NONE, null);
}
}
Be careful not to do the layers in dispatchDraw()
itself as that will crash the app.