如何在没有代码的情况下隐藏 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
或者作为最后的手段之一:ViewFlipper
、ListView
、GridView
、StackView
或 AdapterViewFlipper
layout_width
和 layout_height
是动态的
在示例中,我修复了它,因此很容易尝试不同的变体。上图是layout_height
更改为显示值,模拟我要处理的可能性。
我用过TextView
,但是应该普遍适用于任何View
细心的人可能已经注意到,上述限制了Home screen app widgets with RemoteViews
的解决方案。
尝试
我尝试使用 RelativeLayout
和 LinearLayout
实现,但它们都表现为图片上的实际列。
相对布局
<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
小部件单元,但我无法接近那个数字。我认为它给了我在<appwidget-provider>
上指定的minHeight
。不过,我会再试一次这些 MIN/MAX 并报告。
我再次检查(所有值都是dp
):“onAppWidgetOptionsChanged(appWidgetID, Bundle[appWidgetMaxHeight=89, appWidgetCategory=1, appWidgetMaxWidth=89, appWidgetMinHeight=68, appWidgetMinWidth=68 ])”。 68x68
—89x89
与84x105
相差甚远。
是的,小部件就是这样。它们给你的尺寸是近似的,它们甚至在设备之间或从启动器到启动器都不稳定。【参考方案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
这正是我对小部件的经验观察单元格大小。
给定的边界取决于viewId
给setOnClickPendingIntent
,因此要获得单元格大小,需要在布局中的根视图上附加待处理的意图。我的小部件有一个“点击刷新”侦听器,我可以安全地将其附加到根布局。还有其他视图确实有PendingIntent
s,但它们覆盖了根布局,因此它们的意图将被广播,就像普通的onClickListener
s 一样。
解决方案
现在,这意味着我们知道了确切的单元格大小,基于此可以手动计算某些视图的大小(对于更简单的布局)。本质上我们有办法获得root.getHeight()
,我们可以将first
的大小“硬编码”成R.dimen.firstHeight
,由此我们可以计算出是否显示first
、second
或者没有。
或者,像@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 中的部分可见视图?的主要内容,如果未能解决你的问题,请参考以下文章