如何使用自定义视图创建通知,但具有原生外观?

and*_*per 9 android android-custom-view android-layout android-notifications remoteview

背景

我需要制作一个自定义的大型通知,底部有大约3个图像,顶部有图像标题和2个文本视图.

像这样的东西:

在此输入图像描述

上部区域需要看起来与本机通知完全相同,以使它在两者之间有一个很好的过渡.

底部区域是动态的,照片放在那里(使用位图).

问题

一旦我使用自定义视图,它实际上包含整个布局,包括上部区域.

我试过的

我想到了两个解决方案:

  1. 创建一个位图,在其中包含3个图像,然后使用BigPictureStyle.问题是它可能会在某些情况下裁剪图像的内容,如果你去横向画面,图像之间的间距将不会很好地显示(我已经检查过:你不能设置多个布局,例如一个用于风景和一个用于肖像).

为了创建位图,我可以使用这样的东西:

    final int widthScreen = getResources().getDisplayMetrics().widthPixels;
    final int width = Math.min(widthScreen, (int) convertDpToPixels(this, 256));
    final Bitmap outputBitmap = Bitmap.createBitmap(width, width / 3, Config.ARGB_8888);
    final Canvas canvas = new Canvas(outputBitmap);
    final int[] imagesResIds = { R.drawable.pic1, R.drawable.pic2, R.drawable.pic3 };
    final Paint paint = new Paint();
    for (int i = 0; i < 3; ++i) {
        final Bitmap b = BitmapFactory.decodeResource(getResources(), imagesResIds[i]);
        canvas.drawBitmap(b, null, new Rect(i * (width / 3), 0, (i + 1) * (width / 3) - 1, width / 3 - 1), paint);
    }
    return outputBitmap;
Run Code Online (Sandbox Code Playgroud)

出于某种原因,它没有很好地显示图像,但你明白了......

我也试图在#2上夸大视图,并绘制到位图.它可以工作,但在某些设备和配置(甚至是风景)上它不能很好地工作.

  1. 使用RemoteViews,然后在ImageViews上设置图像.问题是我需要模仿顶部区域看起来是原生的,但即使我找到它,也不能保证它在所有设备和Android版本上看起来都很好(对吧?).另外,我可能需要获得每个Android版本的原生通知布局.使用水平listView也可能很好,但它不存在.

以下是通知底部区域的示例布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="128dp"
    android:gravity="center_horizontal"
    android:orientation="horizontal" >

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic1" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic2" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic3" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

我发现了什么

关于在自定义布局上使用样式的文档非常模糊,只需要小心并在多个Android设备上试用它:

警告:使用自定义通知布局时,请特别注意确保自定义布局使用不同的设备方向和分辨率.虽然此建议适用于所有View布局,但对于通知尤为重要,因为通知抽屉中的空间非常有限.不要使自定义布局过于复杂,并确保以各种配置进行测试.

样式文本也是如此:

始终将样式资源用于自定义通知的文本.通知的背景颜色可能因设备和版本而异,使用样式资源可帮助您解决此问题.从Android 2.3开始,系统为标准通知布局文本定义了一种样式.如果您在面向Android 2.3或更高版本的应用程序中使用相同的样式,则可以确保您的文本在显示背景下可见.

他们不告诉使用什么,哪种款式或颜色或任何东西......

这个问题

是否可以创建此类通知?我应该怎么处理?

你还有其他想法可以做到这一点吗?

是否也可以使用限定符设置不同的布局(我没有成功)?

此外,也许是最重要的问题:如何在自定义视图上使用默认的通知样式?我不仅谈论标题和副标题textViews(可能使用"android:TextAppearance.StatusBar.EventContent.Title"和"android:TextAppearance.StatusBar.EventContent"样式),但对于通知的任何部分:图标右边的文字,行动,......


编辑:在搜索通知的样式一段时间后,我在Android的源代码中的"styles.xml"中找到了这个:

<style name="TextAppearance.StatusBar.Icon">
</style>
<!-- Notification content styles -->
<style name="TextAppearance.StatusBar.EventContent">
    <item name="textColor">#999999</item>
    <item name="textSize">@dimen/notification_text_size</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Title">
    <item name="textColor">#ffffff</item>
    <item name="fontFamily">sans-serif-light</item>
    <item name="textSize">@dimen/notification_title_text_size</item>
    <item name="textStyle">bold</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Line2">
    <item name="textSize">@dimen/notification_subtext_size</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Info">
    <item name="textSize">@dimen/notification_subtext_size</item>
    <item name="textColor">#999999</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Time">
    <item name="textSize">@dimen/notification_subtext_size</item>
    <item name="textColor">#999999</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Emphasis">
    <item name="textColor">#CCCCCC</item>
</style>
Run Code Online (Sandbox Code Playgroud)

有一种名为"TextAppearance.StatusBar.Icon"的样式很奇怪,因为它是一个图标,但它是一个textAppearance.再加上它是空的......

无论如何,在那之后,我找到了下一个布局文件,名为"notification_intruder_content.xml",但我不确定是否可以复制它,因为操作系统之间可能会发生变化(甚至在不同的roms之间):

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="4dp"
    >
    <ImageView android:id="@+id/icon"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:scaleType="center"
        android:padding="4dp"
        />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginStart="40dp"
        android:orientation="vertical"
        >
        <TextView android:id="@+id/title"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            />
        <TextView android:id="@+id/text"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginTop="-4dp"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/actions"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:orientation="horizontal"
        android:visibility="gone"
        >
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action0"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
    </LinearLayout>
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

我试图使用它(只是更改ID,并设置文本和图像),但它看起来并不好:

在此输入图像描述

不仅如此,但在触摸它时,我看不到触摸正常通知的效果,因此背景也可能是错误的.


编辑:遗憾的是它决定使用自定义视图解决方案(#2),但我找不到有关如何使所有内容看起来都是原生的足够信息,所以我不得不做一些"逆向工程"看着Android的代码以及通知的外观.

如果有人知道如何让它在所有Android版本(和roms)中看起来都很好,请告诉我.

app*_*krb 5

我没有得到Exact输出,试试这个

创建通知:

public void CustomNotification() {
        //dynamic layer
        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.HORIZONTAL);

        LayoutParams llLP = new LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        ll.setLayoutParams(llLP);
        ImageView imageView = new ImageView(this);
        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
        imageView.setLayoutParams(lp);
        imageView.setImageResource(R.drawable.v1);

        ll.addView(imageView);
        ImageView imageView1 = new ImageView(this);

        imageView1.setLayoutParams(lp);
        imageView1.setImageResource(R.drawable.v2);

        ll.addView(imageView1);
        //create a dynamic layout to bitmap
        Bitmap bp = convertViewToDrawable(ll);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                this).setSmallIcon(R.drawable.ic_launcher)
                .setTicker("Dummy App ").setAutoCancel(true)
                .setContentIntent(null);
        NotificationCompat.BigPictureStyle bigPicStyle = new NotificationCompat.BigPictureStyle();
        bigPicStyle.bigPicture(bp);
        bigPicStyle.setBigContentTitle("My Title");
        builder.setStyle(bigPicStyle);

        NotificationManager notificationmanager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationmanager.notify(100, builder.build());

    }
Run Code Online (Sandbox Code Playgroud)

创建视图到位图

public static Bitmap convertViewToDrawable(View view) {
        int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(),
                view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        c.translate(-view.getScrollX(), -view.getScrollY());

        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        view.destroyDrawingCache();
        return viewBmp;

    }
Run Code Online (Sandbox Code Playgroud)

输出:

产量