【问题标题】:Center Android WindowBackground with Activity ImageView使用 Activity ImageView 居中 Android WindowBackground
【发布时间】:2019-03-03 01:47:26
【问题描述】:

上下文

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

第 2 步: 加载活动后,它会执行简单的 setContentView 设置活动的背景,现在将替换 step1 中的 android:windowBackground。这会加载一个应该居中对齐的imageView

问题是它们都不是中心对齐的,其中一个或另一个未对齐它们存在某种偏移。可能状态栏向下推一个?我不知道。有什么想法为什么它们不一致?

我想让它们都居中对齐。我试过使用fitSystemWindows="true",但没有成功。

当我将 android:layout_marginTop="12dp" 添加到活动 (activity_start_onboarding.xml) 布局 imageView 时,两者都可以很好地对齐,但它并不能针对所有密度对齐,其他密度也不会对齐。

也许有一种方法可以动态计算此偏移量以对齐所有密度?


比较图片

左(灰色)- Step1(窗口背景):
右(蓝色)- Step2(活动布局):


代码

AndroidManifest.xml

<activity android:name=".view.onboarding.ActivityStartOnboarding"
            android:launchMode="singleTask"
            android:screenOrientation="portrait"
            android:theme="@style/ColdstartSplashTheme">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

styles.xml

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

@drawable/splash_background

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/grey_mid_dark" />

<item>
    <bitmap
        android:gravity="center"
        android:src="@drawable/ic_delivery_free_raster" />
</item>

ActivityStartOnboarding.java

public class ActivityStartOnboarding extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_start_onboarding);
  }
}

activity_start_onboarding.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/blue_light_darker"
        android:fitsSystemWindows="true">

        <androidx.appcompat.widget.AppCompatImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/splash_background" />
    </FrameLayout>
</layout>

【问题讨论】:

  • 为什么在activity_start_onboarding.xml 中使用splash_background 而在splash_background 中使用ic_delivery_free_raster
  • 您好,仅供参考。以便主题/样式使用与活动相同的布局。 @drawable/splash_background

标签: android splash-screen


【解决方案1】:

对我来说,接受的解决方案不起作用。但我找到了this,它就像一个魅力。 它也是一个更灵活的解决方案,因为它不依赖于 API 21

【讨论】:

    【解决方案2】:

    使用背景代替windowBackground

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

    【讨论】:

    • 您好,感谢您的意见!当我使用android:background 而不是android:windowBackground 时,setContentView(R.layout.activity_start_onboarding); 没有任何效果,所以我看不到第二个屏幕(Step2/蓝屏)。有什么想法吗?
    • @Francois 嗨!我昨天回答了(stackoverflow.com/a/54998465/8971623)。你试过我的解决方案了吗?它应该工作
    • 嗨,我明天去看看 :) tx!
    【解决方案3】:
    android:fitsSystemWindows="true"
    

    那是你的问题。您的 FrameLayout 在状态栏下开始膨胀,高度约为 25dp,因此 AppCompatImageView 高一点。

    第一个解决方案: 从 activity_start_onboarding.xml FrameLayout 中删除 android:fitsSystemWindows="true"

    第二种解决方案: 将&lt;item name="android:windowDrawsSystemBarBackgrounds"&gt;true&lt;/item&gt; 添加到您的ColdstartSplashTheme。背景在状态栏下开始绘制,位图会更高。

    【讨论】:

    • 抱歉回复慢! 1. 删除android:fitsSystemWindows="true" 实际上会使其画得稍微低一些。拉近它们 2. &lt;item name="android:windowDrawsSystemBarBackgrounds"&gt;true&lt;/item&gt; 实际上确实拉得更高,现在两者都完美对齐。
    • 感谢您的出色回答,很抱歉没有尽快回来:O
    【解决方案4】:

    无论出于何种原因,其他解决方案都不适合我。因此,我编写了一个类似于 ImageView 的快速类,不同之处在于 它将提供的可绘制对象相对于整个窗口居中,而不是在它自己的视图范围内。

    基本上windowBackground 在您的主题中使用窗口 的边界。你的ImageView,即使它填充了它的父级,仍然受到你的 activity 边界的限制,这是一个不同的形状(例如,不包括系统栏)。因此,在 window 中将 drawable 居中(windowBackground 所做的)与在 view 中将 drawable 居中(ImageView 所做的)会产生不同的结果。我编写的View 类将drawable 放在窗口 的中心,所以它的作用与您的主题相同。

    课程如下:

    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 中:
        <declare-styleable name="WindowCenteredImageView">
            <!-- Sets a drawable as the content of this ImageView. -->
            <attr name="src" format="reference" />
        </declare-styleable>
    
    • 现在用这个替换你的 ImageView(它需要比它要绘制的 drawable 大,因为包装内容可能无法正常工作:我刚刚使用 match_parent 将此视图设置为活动的整个大小 -在你给它的drawable之外,它将是透明的):
    <your.package.name.WindowCenteredImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:src="@drawable/ic_delivery_free_raster"/>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多