如何在特定区域覆盖列表项文本颜色?

8
假设我们有一个自定义的 ListView,它通过在其中绘制一些矩形来扩展 onDraw() 方法。
class Listpicker extends ListView {
//..Some other methods here..//
    @Override
    protected void onDraw(android.graphics.Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
    }
}

所绘制的位图代表一个类似于玻璃的矩形,它的宽度与列表视图的宽度相同,高度是任意的。我希望当列表项滚动到该矩形中时,用于绘制文本的颜色(而不是所有具有背景等的项目)将更改为另一种颜色。例如,当列表项适合使得文本的一半高度只适合于 glassPickerBitmap 时,列表项的外部部分应保持其原始颜色,而在 glassPickerBitmap 内部的部分应更改其文本颜色。列表项是简单的没有任何背景的 TextView。

如何实现这个功能?也许可以将任何 ColorFilter 分配给 Paint?但是在哪里?当 ListView 滚动时,甚至不会调用 onDraw() 方法...是否可以在自定义视图内完成此操作,例如,作为 ListView 的项?或者可以在此处使用某些覆盖层(如 Color/Shader 等),但是在哪里

编辑(添加图像示例):

这是来自真实应用程序的一些裁剪示例。有两个 ListView:一个在左侧,另一个在右侧。玻璃是灰色的矩形。现在,左侧列表选择了“美元”货币。我以这样的方式向右滚动 ListView,使选择位于 美元 (USD)阿富汗阿富汗尼 之间。现在,我想要的是,在同一时间内,左侧 ListView 中的 美元 (USD) 货币应以红色绘制(确切的颜色现在并不重要,稍后会将其更改为有意义的颜色),并且在右侧 ListView 中的 “美元”下面部分和“阿富汗阿富汗尼”上面部分也应使用相同的红色绘制。

这种颜色变化应以动态方式完成-只有在列表滚动时,文本部分才应更改为玻璃矩形下方的部分。

*好吧,这里的 EUR 和 USD 是用非标准青色绘制的特殊货币。问题至少涉及白色文本。但如果可以更改青色,那就太好了。

List example


View.setWillNotDraw(boolean willNotDraw)应该会在ListView中调用onDraw。然而,这是否能够帮助您实现您想要的目标还有待商榷。不过这是一个有趣的问题。 - techi.services
@Prizoff,你能提供两张图片吗?一张是普通物品的图片,另一张是玻璃下方物品的图片。 - eveliotc
@Prizoff 很酷,让我看看。 - eveliotc
@Prizoff,你能看一下我的答案吗? - eveliotc
@EvelioTarazona 我一定会看一下的,只是现在不能使用任何编程工具,因为有其他非IT相关的问题。初步看起来应该可以,谢谢! - Prizoff
2个回答

3

从它们的实现中可以看出,它们在ListAdapter中使用并更新了一个固定位置,然后相应地更新与之匹配的View,这对我来说是最容易和简单的方法,但你不会得到半半彩色文本,我认为你仍然想要 :)


http://i.imgur.com/jHvuBcL.png

所以这可能是我给出的最糟糕的答案之一,对此感到抱歉,我可能会重新审视这个问题,或者希望有人能指向更好的解决方案

你可以在以下网址查看模糊演示:

http://telly.com/Y98DS5

粗暴的方法:

  • 创建一个具有适当的ColorMatrixColorFilter的Paint,为了回答这个问题,我只是将其去饱和,你需要为ColorMatrix找出5x4矩阵,以获得你想要的青色。此外,您还需要一些Rect来定义绘制时的源和目标。
  • 创建一个离屏Bitmap和Canvas,您将使用它来绘制列表,这在onSizeChanged中完成,以便我们有正确的视图布局值,这里您可以开始注意到它的粗糙性,因为您将不得不分配一个与ListView一样大的Bitmap,所以您可能会获得OutOfMemoryException。
  • 重写draw以使用您的离屏Canvas并让ListView将其绘制到其中,然后您可以将Bitmap绘制到给定的Canvas中,如常规列表一样绘制您的列表,然后仅绘制该Bitmap的部分,该部分将以不同的方式绘制使用我们之前定义的Paint,这些像素将被覆盖绘制。
  • 最后绘制预制的玻璃Bitmap,我建议使用n-patch(9-patch),以便在屏幕和密度之间进行缩放并显示清晰,然后您将获得更多的覆盖绘制像素。

整个过程看起来像:

public class OverlayView extends ListView {
  private RectF mRect;
  private Rect mSrcRect;
  private Paint mPaint;

  private Canvas mCanvas;
  private Bitmap mBitmap;
  private float mY;
  private float mItemHeight;

  public OverlayView(Context context) {
    super(context);

    initOverlay();
  }

  public OverlayView(Context context, AttributeSet attrs) {
    super(context, attrs);

    initOverlay();
  }

  public OverlayView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    initOverlay();
  }

  private void initOverlay() {
    // DO NOT DO THIS use attrs
    final Resources res = getResources();
    mY = res.getDimension(R.dimen.overlay_y);
    mItemHeight = res.getDimension(R.dimen.item_height);
    //
    mRect = new RectF();
    mSrcRect = new Rect();
    mPaint = new Paint();

    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(0);
    mPaint.setColorFilter( new ColorMatrixColorFilter(colorMatrix) );
  }

  @Override
  public void draw(Canvas canvas) {
    super.draw(mCanvas);
    canvas.drawBitmap(mBitmap, 0, 0, null);

    canvas.drawBitmap(mBitmap, mSrcRect, mRect, mPaint);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    mRect.set(0, mY, w, mY + mItemHeight);
    mRect.roundOut(mSrcRect);

    mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);
  }
}

因此,我希望您最终能够得到一个更干净的解决方案,即使您没有获得那种半透明的性感效果 :)

是的,我使用Max OS X,确切地说是Mavericks版本。 - eveliotc
使用getDrawingCache()代替单独的位图怎么样? - S.D.
@S.D. 是的,你也可以使用它,实际上它做了类似的事情,只是带有一些额外的怪癖,比如现在已经弃用的最大缓存大小、空位图,其他人也可以使用那个绘图缓存,而且我记得你应该只在软件层面上使用它,在这种情况下(并不总是)管理自己的缓存可以让你掌控和放心:你确定这是你的问题;无论如何,正如我之前提到的,这是肮脏的,你应该避免它(最糟糕的推销员)。 - eveliotc
@EvelioTarazona 它绝对有效,谢谢!起初,我试图通过覆盖 onDraw() 而不是 draw() 来使其工作。但当我注意到这一点时,情况变得更好了。 :) - Prizoff
@Prizoff 非常荣幸,上次我做了一个类似的东西,有人说“等着有人拿着斧头跳出来”。 - eveliotc

1
首先,保留备用位图和画布:

First, keep a spare bitmap and canvas:

class Listpicker extends ListView {
    Bitmap bitmapForOnDraw = null;
    Canvas canvasForOnDraw = null;

确保它是正确的尺寸:

    @override
    protected void onSizeChanged (int w, int h, int oldw, int oldh) {
        if (bitmapForOnDraw != null) bitmapForOnDraw.recycle();
        bitmapForOnDraw = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        canvasForOnDraw = new Canvas(bitmapForOnDraw);
    }

当我们来绘制列表时:
    @override
    protected void onDraw(android.graphics.Canvas realCanvas) {
        // Put the list in place
        super.onDraw(realCanvas);

        // Now get the same again
        canvasForOnDraw.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
        super.onDraw(canvasForOnDraw);
        // But now change the colour of the white text
        canvasForOnDraw.drawColor(0xffff00ff, PorterDuff.Mode.MULTIPLY);

        // Copy the different colour text into the right place
        canvas.drawBitmap(bitmapForOnDraw, glassRect, glassRect overwritePaint);

        // finally, put the box on.
        canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接