Android - 运行时更改自定义标题视图

23
我在我的应用程序中为每个活动使用自定义标题视图。在其中一个活动中,根据按钮点击,我需要更改自定义标题视图。现在,每次调用setFeatureInt时都可以正常工作。
但是,如果我尝试更新自定义标题中的任何项目(例如更改标题上按钮或文本视图的文本),则不会进行更新。
通过代码调试可以显示文本视图和按钮实例不为空,也可以看到自定义标题栏。但是文本视图或按钮上的文本未更新。有人遇到过这个问题吗?如何解决?
谢谢。
编辑:
这是我尝试的内容。即使调用postInvalidate,也不会进行更新。
    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.text_title);

    TextView databar = (TextView) findViewById(R.id.title_text);
    databar.setText("Some Text");
    databar.postInvalidate();

    Button leftButton = (Button) findViewById(R.id.left_btn);
    leftButton.setOnClickListener(mLeftListener);
    leftButton.setText("Left Btn");
    leftButton.postInvalidate();

    Button rightBtn = (Button) findViewById(R.id.right_btn);
    rightBtn.setOnClickListener(mRightListener);
    rightBtn.postInvalidate();

请参考以下链接以获取替代解决方案:https://dev59.com/_17Va4cB1Zd3GeqPLqS1#9394561 - Tofeeq Ahmad
4个回答

32
问题在于唯一的Window实现(PhoneWindow)在其setFeatureInt方法中使用LayoutInflater并使用inflateattachToRoot=true来实例化新布局。因此,当您调用setFeatureInt时,新布局不是被替换而是附加到内部标题容器上,从而彼此叠加绘制。
您可以通过使用以下辅助方法来解决此问题,而不是使用setFeatureInt。该辅助程序仅在设置新的自定义标题功能之前从内部标题容器中删除所有视图:

private void setCustomTitleFeatureInt(int value) {
    try {
        // retrieve value for com.android.internal.R.id.title_container(=0x1020149)
        int titleContainerId = (Integer) Class.forName(
            "com.android.internal.R$id").getField("title_container").get(null);

        // remove all views from titleContainer
        ((ViewGroup) getWindow().findViewById(titleContainerId)).removeAllViews();

        // add new custom title view 
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, value);

    } catch(Exception ex) {
        // whatever you want to do here..
    }
}

我不确定当前的setFeatureInt行为是否是有意的,但肯定没有一种方式或另一种方式记录它,这就是为什么我会把它带给android开发者的原因;) 编辑 如评论中所指出的那样,上述解决方法并不理想。而你可以在设置新标题时简单地隐藏旧自定义标题,而不是依赖于com.android.internal.R.id.title_container常量。
假设您有两个自定义标题布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/custom_title_1" ...

并且

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/custom_title_2" ...

如果你想用custom_title_2替换custom_title_1,你可以隐藏前者并使用setFeatureInt添加后者:

findViewById(R.id.custom_title_1).setVisibility(View.GONE);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_2);

1
如果这些值(title_container、com.android.internal)在后续版本中发生了变化,那么这会影响应用程序吗? - lostInTransit
1
你是绝对正确的。尽管com.android.internal.title_container常量很可能不会改变,但依赖它是错误的。这只是一个丑陋的解决方法。 - Josef Pfleger
谢谢。这个方案很好用。但我真的不明白为什么setFeatureInt会将视图一层层地添加在上面,而不是替换它们?尤其是那些必须是单数的视图,比如标题! - lostInTransit
你还能在标题栏中放置进度条吗?如果可以,你会给标题中的进度条分配什么ID呢? - hwrdprkns
@hwrdprkns 当然可以,只需要将进度条部分放在上面描述的 <RelativeLayout/> 中即可。 - Josef Pfleger
更好的写法: int titleContainerId = getResources().getIdentifier("android:id/title_container", null, null); - Pointer Null

14

正确的做法如下:

requestWindowFeature( Window.FEATURE_CUSTOM_TITLE );
setContentView( R.layout.my_layout );
getWindow().setFeatureInt( Window.FEATURE_CUSTOM_TITLE, R.layout.my_custom_title );
super.onCreate( savedInstanceState );
请注意这些语句的顺序非常重要。
如果在任何其他语句之前调用super.onCreate(),则会得到一个空白标题栏,通过查找标题栏ID并将所有视图从中删除的hack可以解决此问题,但不建议使用。

OP正在尝试在onCreate()之后的运行时更改标题栏。 - CACuzcatlan

0

仅供参考:

在MapActivity中工作时,请求自定义标题会导致根本没有显示标题。

幸运的是,我只是想以不同的方式设置标题文本,很快就意识到在onCreate()内部调用setTitle()对我有用(在调用setContentView()后调用它)

很抱歉,我现在没有时间再调试此问题并弄清楚为什么我所做的事情不起作用,以及更改为什么使其起作用。正如我所说,我认为这可能会帮助某些人解决问题。


0
你是否在更新文本后调用invalidate或postInvalidate来重新绘制视图?如果这是一个自定义视图,你可以在绘制代码中设置断点以确保它被调用了吗?
如果你在UI线程上,你可以调用“invalidate”,否则你必须调用“postInvalidate”,否则视图不会重新绘制。

如果有帮助的话,视图会重新绘制,因为我看到了更新后的标题栏,但标题栏上的控件没有改变。 - lostInTransit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接