使用活动图像视图将Android WindowBackground居中 上下文

使用活动图像视图将Android WindowBackground居中 上下文,android,splash-screen,Android,Splash Screen,第1步:目前我有一个活动,它使用android:windowBackground在我们等待活动加载时设置初始背景。这将加载一个应该居中对齐的位图 Step2:一旦加载活动,它会执行一个简单的setContentView来设置活动的背景,该背景现在将取代android:windowBackground中的step1。这将加载一个应居中对齐的imageView 问题是它们不是都对准了中心,一个或另一个上存在某种偏移,使它们不对准。可能是状态栏按下了那个?我不确定。你知道为什么它们不一致吗 我想让它们

第1步:目前我有一个活动,它使用
android:windowBackground
在我们等待活动加载时设置初始背景。这将加载一个应该居中对齐的位图

Step2:一旦加载活动,它会执行一个简单的
setContentView
来设置活动的背景,该背景现在将取代
android:windowBackground
中的step1。这将加载一个应居中对齐的
imageView

问题是它们不是都对准了中心,一个或另一个上存在某种偏移,使它们不对准。可能是状态栏按下了那个?我不确定。你知道为什么它们不一致吗

我想让它们都居中对齐。我尝试过使用
fitSystemWindows=“true”
,但运气不佳

当我将
android:layout\u marginTop=“12dp”
添加到活动(
activity\u start\u onboarding.xml
)布局
imageView
两者对齐良好,但并非所有密度都对齐,其他密度未对齐

是否有一种方法可以动态计算此偏移以对齐所有密度


比较图像 左侧(灰色)-步骤1(窗口背景):
右侧(蓝色)-步骤2(活动布局):

代码 AndroidManifest.xml


styles.xml


@可绘制/飞溅背景
@可绘制/飞溅\u背景


活动StartOnboard.java

公共类活动StartOnboard扩展AppCompative活动{
@凌驾
创建时受保护的void(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u start\u Onboard);
}
}
activity\u start\u onboard.xml


这是你的问题。您的FrameLayout在statusbar下开始膨胀,其高度约为25dp,因此AppCompatImageView稍高一些

第一个解决方案: 从
activity\u start\u onboarding.xml
FrameLayout中删除
android:fitsystemwindows=“true”

第二种解决方案:
true
添加到您的
ColdstartSplashTheme
。背景在状态栏下开始绘制,位图将更高。

使用背景而不是windowBackground

<style name="ColdstartSplashTheme" parent="SuperbalistTheme.Dark">
    <item name="android:background">@drawable/splash_background</item>
</style>

@可绘制/飞溅背景

对我来说,接受的解决方案不起作用。但我发现了,它就像一个符咒。
这也是一个更灵活的解决方案,因为它不依赖于API 21,无论出于何种原因,其他任何解决方案都不适合我。因此,我编写了一个类似于ImageView的quick类,只是它将给定的可绘制对象相对于整个窗口居中,而不是在其自身的视图范围内

主题中的
windowBackground
基本上使用了窗口的边界。您的
图像视图
,即使它填充了其父视图,仍然受到活动的限制,这是一种不同的形状(例如,不包括系统栏)。因此,在窗口中居中放置一个可绘制图形(如
windowBackground所做的)与在视图中居中放置一个可绘制图形(如
图像视图所做的)会产生不同的结果。我编写的
视图
类在窗口中居中绘制,因此它的功能与主题相同

下面是课程:

package your.package.name

import android.app.Activity
import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import your.package.name.R

/**
 * Simply draws the given drawable in the center of the current window with no scaling. This is not
 * very sophisticated, so cannot handle a lot of the features that a regular [ImageView] can. If
 * these features are required, you might want to try subclassing [ImageView].
 */
class WindowCenteredImageView(context: Context, attrs: AttributeSet): View(context, attrs) {

    // Obtain drawable from attributes.
    private val d: Drawable?
    init {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.WindowCenteredImageView,
            0, 0
        ).apply {
            try {
                d = getDrawable(R.styleable.WindowCenteredImageView_src)
            } finally {
                recycle()
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        if (d != null) {
            // We have a drawable, so try to draw it!
            if (d.intrinsicWidth >= 0 && d.intrinsicHeight >= 0) {
                // Our drawable doesn't fill the screen, so we should calculate its bounds. This is
                // where we take into account the current view's offset within the window...
                val viewTopLeftRelativeToWindow = onDrawCachedObjects.point1.apply {
                    val array = onDrawCachedObjects.array.apply { getLocationInWindow(this) }
                    set(array[0], array[1])
                }

                // And the window's dimensions...
                // (Casting here can't fail as views are always given activity context.)
                val window = (context as Activity).window.decorView
                val windowHeight = window.height
                val windowWidth = window.width

                // Do the calculations.
                // First, find out where we want the image to go in the window
                val imageTopLeftRelativeToWindow = onDrawCachedObjects.point2.apply {
                    set(
                        (windowWidth / 2) - (d.intrinsicWidth / 2),
                        (windowHeight / 2) - (d.intrinsicHeight / 2)
                    )
                }

                // Now we can calculate where the image should go in the view
                val imageTopLeftRelativeToView = onDrawCachedObjects.point3.apply {
                    set(
                        imageTopLeftRelativeToWindow.x - viewTopLeftRelativeToWindow.x,
                        imageTopLeftRelativeToWindow.y - viewTopLeftRelativeToWindow.y
                    )
                }

                // We have all the information needed to set the image bounds
                d.setBounds(
                    imageTopLeftRelativeToView.x,
                    imageTopLeftRelativeToView.y,
                    imageTopLeftRelativeToView.x + d.intrinsicWidth,
                    imageTopLeftRelativeToView.y + d.intrinsicHeight
                )
            }

            // Draw the drawable onto the canvas
            d.draw(canvas)
        }
    }

    /** Optimization: Objects to use in [onDraw] to avoid instantiations */
    private val onDrawCachedObjects = object {
        val array = IntArray(2)
        val point1 = Point()
        val point2 = Point()
        val point3 = Point()
    }
}
下面是如何使用它:

  • 首先,您需要将其放在res/values/attrs.xml中:

为什么要在
活动\u start\u onboard.xml中使用
splash\u background
,而在
splash\u background
中使用
ic\u delivery\u free\u光栅。因此,主题/样式使用与活动相同的布局
@drawable/splash_background
您好,谢谢您的输入!当我使用
android:background
而不是
android:windowBackground
setContentView(R.layout.activity\u start\u onboarding)时无效,因此我从未看到第二个屏幕(Step2/蓝屏)。有什么想法吗?@Francois嗨!我昨天回答了()。你试过我的解决方法吗?它应该会工作的嗨,我明天会看一看:)tx!对不起,回复太慢了!1.删除android:fitsSystemWindows=“true”
实际上会使它的绘制略低。让他们更接近2<代码>真的
事实上确实把它调高了,现在两者都完美地对齐了。谢谢你的回答,我为没有早点回来而道歉:哦
package your.package.name

import android.app.Activity
import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import your.package.name.R

/**
 * Simply draws the given drawable in the center of the current window with no scaling. This is not
 * very sophisticated, so cannot handle a lot of the features that a regular [ImageView] can. If
 * these features are required, you might want to try subclassing [ImageView].
 */
class WindowCenteredImageView(context: Context, attrs: AttributeSet): View(context, attrs) {

    // Obtain drawable from attributes.
    private val d: Drawable?
    init {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.WindowCenteredImageView,
            0, 0
        ).apply {
            try {
                d = getDrawable(R.styleable.WindowCenteredImageView_src)
            } finally {
                recycle()
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        if (d != null) {
            // We have a drawable, so try to draw it!
            if (d.intrinsicWidth >= 0 && d.intrinsicHeight >= 0) {
                // Our drawable doesn't fill the screen, so we should calculate its bounds. This is
                // where we take into account the current view's offset within the window...
                val viewTopLeftRelativeToWindow = onDrawCachedObjects.point1.apply {
                    val array = onDrawCachedObjects.array.apply { getLocationInWindow(this) }
                    set(array[0], array[1])
                }

                // And the window's dimensions...
                // (Casting here can't fail as views are always given activity context.)
                val window = (context as Activity).window.decorView
                val windowHeight = window.height
                val windowWidth = window.width

                // Do the calculations.
                // First, find out where we want the image to go in the window
                val imageTopLeftRelativeToWindow = onDrawCachedObjects.point2.apply {
                    set(
                        (windowWidth / 2) - (d.intrinsicWidth / 2),
                        (windowHeight / 2) - (d.intrinsicHeight / 2)
                    )
                }

                // Now we can calculate where the image should go in the view
                val imageTopLeftRelativeToView = onDrawCachedObjects.point3.apply {
                    set(
                        imageTopLeftRelativeToWindow.x - viewTopLeftRelativeToWindow.x,
                        imageTopLeftRelativeToWindow.y - viewTopLeftRelativeToWindow.y
                    )
                }

                // We have all the information needed to set the image bounds
                d.setBounds(
                    imageTopLeftRelativeToView.x,
                    imageTopLeftRelativeToView.y,
                    imageTopLeftRelativeToView.x + d.intrinsicWidth,
                    imageTopLeftRelativeToView.y + d.intrinsicHeight
                )
            }

            // Draw the drawable onto the canvas
            d.draw(canvas)
        }
    }

    /** Optimization: Objects to use in [onDraw] to avoid instantiations */
    private val onDrawCachedObjects = object {
        val array = IntArray(2)
        val point1 = Point()
        val point2 = Point()
        val point3 = Point()
    }
}
    <declare-styleable name="WindowCenteredImageView">
        <!-- Sets a drawable as the content of this ImageView. -->
        <attr name="src" format="reference" />
    </declare-styleable>
<your.package.name.WindowCenteredImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:src="@drawable/ic_delivery_free_raster"/>