为视图设置相同的 id 后,findViewByID 返回 null

Posted

技术标签:

【中文标题】为视图设置相同的 id 后,findViewByID 返回 null【英文标题】:findViewByID returns null after setting same id to the view 【发布时间】:2016-03-17 19:22:59 【问题描述】:

我在 onCreate() 中膨胀 layout,这已在我的活动中声明:

layout = (RelativeLayout) findViewById(R.id.layout);

假设 id 在我的活动中被声明为 int

稍后,在某些事件之后,我将一个图像视图添加到此 布局

id = (int) Calendar.getInstance().getTimeInMillis();
ImageView imageView = new ImageView(context);
imageView.setImageResource(R.drawable.ic_launcher);    
imageView.setId(id);
layout.addView(imageView);

稍后,我想从我们之前设置的 id 中获取 imageView:

ImageView imageView = (ImageView) findViewById(id);
if (imageView == null)
    Log.e("Test", "imageview is null");

所有代码都成功运行,Imageview 总是返回 null,如日志中打印的那样。

注意:我无法保留 imageview 本身的对象,因为我在实际项目中有许多不同的视图。在这里,我使用单个 imageview 描述了我的问题。我已经存储了视图的所有 ID,但无法使用 findViewById() 获取所有这些视图。我之所以使用 Calendar.getInstance().getTimeInMillis() 来生成 id 是因为我每次都想要一个唯一的 id。我已经存储了所有布局数据,包括 id 以供以后使用。如果用户再次打开此活动,他将从他离开的地方开始。因此,在添加任何新的 imageview 时,不得重复之前生成的 id。

关键点:如果我将设备日期时间提前 2-3 天或更早,那么它可以正常工作。我认为问题在于使用日历生成 id。

【问题讨论】:

正确阅读问题。我不能这样做,因为我使用的是多个视图而不是单个图像视图。 我希望您不会对所有图像视图使用相同的 id,是吗? 显然不是。即使我使用相同,为什么我会得到 null 您能否展示更多代码,例如如何生成和设置 id? 问题中都提到了。 【参考方案1】:

最后,我通过尝试和错误得到了解决方案。 正如我所提到的,它在 2 天前工作正常,我决定使用生成的 id 进行调试,并通过每次减少日期来从long 转换为int。假设计数器在活动中声明为int

            ImageView imageView = new ImageView(context);
            imageView.setImageResource(R.drawable.ic_launcher);
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - counter);
            counter++;
            String date = getDate(calendar.getTimeInMillis(), "dd/MM/yyyy hh:mm:ss.SSS");
            Log.e("Test", "Date :" + date);
            int id = (int) calendar.getTimeInMillis();
            Log.e("Test", "ID:" + id);
            imageView.setId(id);
            layout.addView(imageView);
            imageView = (ImageView) findViewById(id);
            if (imageView == null)
                Log.e("Test", "imageview is null");
            else
                Log.e("Test", "not null");

日志:

03-17 23:13:44.952: E/Test(1469): Date :17/03/2016 11:13:44.952

03-17 23:13:44.952: E/Test(1469): ID:-2018055688

03-17 23:13:44.952: E/Test(1469): imageview is null

03-17 23:13:48.744: E/Test(1469): Date :16/03/2016 11:13:48.745

03-17 23:13:48.744: E/Test(1469): ID:-2104451895

03-17 23:13:48.748: E/Test(1469): imageview is null

03-17 23:13:53.376: E/Test(1469): Date :15/03/2016 11:13:53.380

03-17 23:13:53.376: E/Test(1469): ID:2104120036

03-17 23:13:53.376: E/Test(1469): not null

在这里,我发现我在过去两天得到了 negative int,而在此之前我得到了 positive int。如文档中所述,您应该将 id 设置为正整数。然后我使用以下方法将相同的 id 转换为绝对值:

id = Math.abs(id);

一切正常。

最后,我想知道为什么 setId() 设置为非正数 int 时不会给出异常。他们应该给出例外,因为他们已经提到它应该是正整数,以便任何开发人员都知道问题出在哪里。

【讨论】:

关于id为负时他们应该给出异常的部分,发现这个answer:int必须为正,否则为随意如果我的理解是正确的,如果提供的id是正确的,android自己会生成一个未指定的随机id来代替。【参考方案2】:

如果您不提供需要从中查找视图的容器,例如 View v = findViewById(id),那么这将尝试在使用的 xml 中查找视图。

这样做

ImageView imageView = (ImageView) layout.findViewById(id);
if (imageView == null)
    Log.e("Test", "imageview is null");

注意:

使用它来动态生成 id

int id = System.currentTimeMillis();

getTimeInMillis() 返回日历表示的时间,必要时从其字段重新计算时间。如果未设置时间并且无法从当前字段值计算时间。这可能是你的情况,你不能得到毫秒。

Official doc

【讨论】:

@BrijeshChopda 是 id 一个正整数? 它只是一个 int。 int 可以有正数也可以有负数。 @BrijeshChopda 尝试System.currentTimeMillis(); 生成 id。请查看我的更新答案。【参考方案3】:

当以编程方式生成 id 时,您应该使用以下代码来设置视图的 id,

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) 

   myView.setId(Utils.generateViewId());

 else 

   myView.setId(View.generateViewId());


你可以用它来设置id并测试它吗?

【讨论】:

我不能使用它,因为当用户再次进入活动并且其他视图可能具有这些 id 时,它们会从 1、2、3 之类的开头生成 id。跨度> 【参考方案4】:

好的,所以我去社区四处看看,看到了有关如何在 R.id 中动态添加 id 以供 findViewById() 使用的帖子。起初我认为您应该继续使用View.generateViewId(),但正如您在其中一个答案中评论的那样,您不喜欢使用它。因此,我选择了您的实现并进行了尝试,并添加了一些日志等。首先,这是我的简单活动代码:

MainActivity.java

package com.example.my.myapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout llmain = (LinearLayout) findViewById(R.id.ll_main);

        int id = (int) System.currentTimeMillis();
        Log.d("SAMPLE", "Generated ID: " + id);
        ImageView imageView = new ImageView(this);
        imageView.setImageResource(R.drawable.sample_img);
        imageView.setId(id);
        llmain.addView(imageView);

        Log.d("SAMPLE", "Get ID: " + imageView.getId());

        ImageView iv = (ImageView) findViewById(id);
        if (iv == null) 
            Log.d("SAMPLE", "ID: IMAGE VIEW IS NULL!");
         else 
            Log.d("SAMPLE", "ID: ImageView not null!");
        
    

所以我去试了一下..这里是日志..

03-18 10:54:47.907 29562-29562/com.example.my.myapplication D/SAMPLE: Generated ID: -2019192733
03-18 10:54:47.913 29562-29562/com.example.my.myapplication D/SAMPLE: Get ID: -2019192733
03-18 10:54:47.913 29562-29562/com.example.my.myapplication D/SAMPLE: ID: IMAGE VIEW IS NULL!

然后我从setId()的文档中看到:

标识符应该是一个正数。

请注意,之前生成的 id 是否定。所以我去把id生成改成这样:

int id = Math.abs((int) System.currentTimeMillis());

并尝试再次运行它,这是日志:

03-18 10:58:05.073 32662-32662/? D/SAMPLE: Generated ID: 2018995567
03-18 10:58:05.080 32662-32662/? D/SAMPLE: Get ID: 2018995567
03-18 10:58:05.080 32662-32662/? D/SAMPLE: ID: ImageView not null!

请注意,现在生成的 id 是。现在它能够以某种方式识别它。

【讨论】:

以上是关于为视图设置相同的 id 后,findViewByID 返回 null的主要内容,如果未能解决你的问题,请参考以下文章

constraintsWithVisualFormat 为所有视图设置相同的 Y 位置

如何将相同的viewmodel设置为xamarin表单中的新mvvm中的两个视图

如何将附件视图背景设置为与 UItableViewCell 中的内容视图相同的背景?

为 UITabBarController 子视图控制器设置相同的导航栏?

为膨胀的布局设置视图的可见性消失

React Native image:为啥父视图在设置为“包含”时与“覆盖”保持相同的高度?