如何在没有代码的情况下隐藏 Android 布局 xml 中的部分可见视图?

Posted

技术标签:

【中文标题】如何在没有代码的情况下隐藏 Android 布局 xml 中的部分可见视图?【英文标题】:How to hide partially visible views in Android layout xml without code? 【发布时间】:2015-01-04 07:40:45 【问题描述】:

请在回答之前完整阅读问题!

假设我有两种观点:

    first(黄色)在底部 second(青色)填充上方视图的其余部分first

父视图的大小是动态的。

当父高度动态设置为第一列中的值时,如何实现下面的预期列 UI?请注意部分可见的视图是如何隐藏而不是裁剪的。

约束

任何代码都是不可能的,我知道可以通过以下方式解决问题: second.setVisibility(second.getHeight() < SECONDS_PREFERRED_SIZE? GONE : VISIBLE) 但我无权访问getHeight(),因此解决方案必须是纯 xml。 通过RemoteViews 编写 UI 状态的代码可以放宽。 基于上述不允许自定义视图不允许甚至,只有基本布局管理器 :FrameLayout, LinearLayout, RelativeLayout, GridLayout 或者作为最后的手段之一:ViewFlipperListViewGridViewStackViewAdapterViewFlipper layout_widthlayout_height动态的 在示例中,我修复了它,因此很容易尝试不同的变体。上图是layout_height 更改为显示值,模拟我要处理的可能性。 我用过TextView,但是应该普遍适用于任何View 细心的人可能已经注意到,上述限制了Home screen app widgets with RemoteViews的解决方案。

尝试

我尝试使用 RelativeLayoutLinearLayout 实现,但它们都表现为图片上的实际列。

相对布局

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_ (actually fill_parent, but fix it for sake of simplicity)
    android:layout_ (first column on picture)
    android:layout_gravity="center"
    android:background="#666"
    tools:ignore="HardcodedText,RtlHardcoded"
    >
    <TextView
        android:id="@+id/first"
        android:layout_
        android:layout_
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:background="#8ff0"
        android:text="First"
        />
    <TextView
        android:id="@+id/second"
        android:layout_
        android:layout_
        android:layout_above="@id/first"
        android:background="#80ff"
        android:text="Second"
        android:gravity="center"
        />
</RelativeLayout>

线性布局

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_ (actually fill_parent, but fix it for sake of simplicity)
    android:layout_ (first column on picture)
    android:layout_gravity="center"
    android:background="#666"
    android:orientation="vertical"
    tools:ignore="HardcodedText,RtlHardcoded"
    >
    <TextView
        android:id="@+id/second"
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:background="#80ff"
        android:text="Second"
        android:gravity="center"
        />
    <TextView
        android:id="@+id/first"
        android:layout_
        android:layout_
        android:layout_gravity="right"
        android:background="#8ff0"
        android:text="First"
        />
</LinearLayout>

【问题讨论】:

“我无法访问 getHeight()”为什么? @mmlooloo:因为RemoteViews 上没有View getter。远程进程(小部件主机)没有反馈,这是一种方式,这就是我只询问布局 xml 的原因。 它的 16 加 -24 用于顶部边缘的 8 填充。 @danny117 我不确定我是否理解,您能否通过复制我的尝试来测试它。然后进行建议的更改并通过将layout_height="16dp" 更改为图像上的值来比较视觉效果。 很明显顶部边缘有填充,该填充如何消失?我也对这个谜题的答案感兴趣。 【参考方案1】:

如果这是 RemoteViews/widget,请尝试使用多个布局文件。扩展AppWidgetProvider 并覆盖onAppWidgetOptionsChanged 方法。在此函数中,您需要执行以下操作:

Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
int minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);

然后根据 minHeight 大小写来确定最适合膨胀的布局。 This page 在制作小部件时非常有用。

【讨论】:

多个布局文件是解决此类问题的好主意。 我的目标是 API 10,该方法是 API 16。即使我将它用于较新的版本,我想我也无法获得正确的单元格大小。我知道三星 Galaxy S4 有84dp x 105dp 小部件单元,但我无法接近那个数字。我认为它给了我在&lt;appwidget-provider&gt; 上指定的minHeight。不过,我会再试一次这些 MIN/MAX 并报告。 我再次检查(所有值都是dp):“onAppWidgetOptionsChanged(appWidgetID, Bundle[appWidgetMaxHeight=89, appWidgetCategory=1, appWidgetMaxWidth=89, appWidgetMinHeight=68, appWidgetMinWidth=68 ])”。 68x6889x8984x105 相差甚远。 是的,小部件就是这样。它们给你的尺寸是近似的,它们甚至在设备之间或从启动器到启动器都不稳定。【参考方案2】:

虽然OPTION_APPWIDGET_MIN/MAX_WIDTH/HEIGHT 并没有真正报告实际值,但我找到了解决方法。 请注意,这并不能真正解决手头的问题,而是一个相当复杂的解决方法。

创意来源

Normal update from the widget host
26061-26061/net.twisterrob.app.debug V/TAG﹕ onReceive(Intent  act=android.appwidget.action.APPWIDGET_UPDATE flg=0x10000010 cmp=net.twisterrob.app.debug/net.twisterrob.app.AppWidgetProvider (has extras)  (Bundle[appWidgetIds=[I@42b14090]))  
26061-26061/net.twisterrob.app.debug V/TAG﹕ onUpdate([483])  

User tap with setOnClickPendingIntent
26061-26061/net.twisterrob.app.debug V/TAG﹕ onReceive(Intent  act=android.appwidget.action.APPWIDGET_UPDATE flg=0x10000010 cmp=net.twisterrob.app.debug/net.twisterrob.app.AppWidgetProvider bnds=[281,756][533,1071] (has extras)  (Bundle[appWidgetIds=[I@42b14090]))  
26061-26061/net.twisterrob.app.debug V/TAG﹕ onUpdate([483])

调查

RemoteViews.setOnClickPendingIntent 广播用户点击时,它会放the bounds of the view in the Intent。正如您在bnds=[281,756][533,1071] 中看到的那样,视图是533-281=252 宽和1071-756=315 高,这些是像素,Galaxy S4 是XXHDPI (3x) 设备,所以252x315 / 3 = 84x105 这正是我对小部件的经验观察单元格大小。

给定的边界取决于viewIdsetOnClickPendingIntent,因此要获得单元格大小,需要在布局中的根视图上附加待处理的意图。我的小部件有一个“点击刷新”侦听器,我可以安全地将其附加到根布局。还有其他视图确实有PendingIntents,但它们覆盖了根布局,因此它们的意图将被广播,就像普通的onClickListeners 一样。

解决方案

现在,这意味着我们知道了确切的单元格大小,基于此可以手动计算某些视图的大小(对于更简单的布局)。本质上我们有办法获得root.getHeight(),我们可以将first的大小“硬编码”成R.dimen.firstHeight,由此我们可以计算出是否显示firstsecond或者没有。

或者,像@AndrewOrobator 建议的那样利用这些边界:为适合大小的布局膨胀。

需要用户交互

这需要用户交互至少一次的唯一问题。当小部件主机发送更新时没有边界,因此用户必须点击视图才能获得大小。我会通过强制用户点击一次视图来解决这个问题(就在他们将小部件安装到主页之后):

@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) 
    if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(intent.getAction())) 
        for (int appWidgetId : getAppWidgetIds(intent)) 
            Rect storedBounds = getSizeFromPreferences(appWidgetId);
            Rect sourceBounds = intent.getSourceBounds();
            if (storedBounds == null && sourceBounds != null) 
                // no stored size, but we just received one, save it
                setSizeToPreferences(appWidgetId, sourceBounds);
                storedBounds = sourceBounds;
            
            if (storedBounds == null) 
                // we didn't have a stored size, neither received one
                // force the user to interact with the widget once so we have a size
                RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_tap_to_refresh);
                views.setOnClickPendingIntent(R.id.root, createRefresh(context, appWidgetId));
                AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
             else 
                // update as usual, and use the bounds from getSizeFromPreferences(appWidgetId)
                onUpdate(context, AppWidgetManager.getInstance(context), new int[] appWidgetId);
            
        
     else 
        super.onReceive(context, intent);
    

private @NonNull int[] getAppWidgetIds(Intent intent) 
    int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
    if (appWidgetIds == null) 
        appWidgetIds = new int[0];
    
    return appWidgetIds;

private @NonNull PendingIntent createRefresh(Context context, int appWidgetId) 
    Intent intent = new Intent(context, MyAppWidgetProvider.class);
    intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetId);
    return PendingIntent.getBroadcast(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);

【讨论】:

以上是关于如何在没有代码的情况下隐藏 Android 布局 xml 中的部分可见视图?的主要内容,如果未能解决你的问题,请参考以下文章

键盘布局隐藏android操作栏?

如何调用Android隐藏API

如何在没有代码隐藏的情况下处理 ViewModel 中的 WPF 路由命令?

如何在 Android 中显示/隐藏分组视图?

xml布局系列:代码控制include布局的显示隐藏

在android中如何让布局居中