为什么 RecyclerView 在 ToolBar 上膨胀?

Why is the RecyclerView inflating over the ToolBar?

我在给 recyclerview 充气时遇到问题。 在recyclerview里面,有些卡片膨胀了,出来的时候工具栏消失了,设置按钮也不能用

使用最新的 SDK 和库版本。

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints">
    </androidx.appcompat.widget.Toolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/conteudoRSS"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

linha.xml -> 这里我得到了卡片视图,我用它来显示回收站视图上的项目

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    style="@style/CardView.Light"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp">

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp">

        <TextView
            android:id="@+id/titulo"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:textColor="#000000"
            tools:text="Titulo Noticia"
            app:layout_constraintEnd_toStartOf="@+id/imagem"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/dataPublicacao"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1"
            android:textColor="#8A000000"
            tools:text="18 Setembro 2020"
            app:layout_constraintEnd_toStartOf="@+id/imagem"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/titulo" />

        <ImageView
            android:id="@+id/imagem"
            android:layout_width="140dp"
            android:layout_height="140dp"
            android:layout_margin="12dp"
            tools:background="@color/colorAccent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/url"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:visibility="invisible"
            tools:ignore="MissingConstraints"
            app:layout_constraintTop_toBottomOf="@+id/dataPublicacao" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

MainActivity.java

package br.ufpe.cin.android.rss;

import androidx.appcompat.app.AppCompatActivity;
package br.ufpe.cin.android.rss;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

import com.prof.rssparser.Article;
import com.prof.rssparser.Channel;
import com.prof.rssparser.OnTaskCompleted;
import com.prof.rssparser.Parser;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private String urlFeed;
    private Toolbar mTopToolbar;
    RecyclerView conteudoRSS;
    List<Article> noticias;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conteudoRSS = findViewById(R.id.conteudoRSS);
        mTopToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mTopToolbar);
        SharedPreferences preferences = getSharedPreferences
                ("user_preferences", MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString("feed1", getString(R.string.feed1));
        editor.putString("feed2", getString(R.string.feed2));
        editor.putString("feed3", getString(R.string.feed3));
        editor.apply();

        if(preferences.contains("rssfeed")){
            urlFeed = preferences.getString("rssfeed", getString(R.string.feed_padrao));
        } else {
            editor.putString("rssfeed", getString(R.string.feed_padrao));
            editor.apply();
            urlFeed = preferences.getString("rssfeed", getString(R.string.feed_padrao));
        }
        
        conteudoRSS = new RecyclerView(this);
        conteudoRSS.setLayoutManager(new LinearLayoutManager(this));
        
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            startActivity(new Intent(
                    this, PreferenciasActivity.class));
            return true;
        } else {
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Parser p = new Parser.Builder().build();
        p.onFinish(
                new OnTaskCompleted() {
                    @Override
                    public void onTaskCompleted(Channel channel) {
                        noticias = channel.getArticles();
                        runOnUiThread(
                                () -> {
                                    RssAdapter adapter = new RssAdapter(
                                            getApplicationContext(),
                                            noticias
                                    );
                                    conteudoRSS.setAdapter(adapter);
                                    setContentView(conteudoRSS);
                                }
                        );
                    }
                    @Override
                    public void onError(Exception e) {
                        Log.e("RSS_APP",e.getMessage());
                    }
                }
        );
        p.execute(urlFeed);
    }
    protected void onResume() {
        super.onResume();
    }

    private String getRssFeed(String feed) throws IOException {
        InputStream in = null;
        String rssFeed;
        try {
            URL url = new URL(feed);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            in = conn.getInputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            for (int count; (count = in.read(buffer)) != -1; ) {
                out.write(buffer, 0, count);
            }
            byte[] response = out.toByteArray();
            rssFeed = new String(response, StandardCharsets.UTF_8);
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return rssFeed;
    }
}
        conteudoRSS = new RecyclerView(this);
        conteudoRSS.setLayoutManager(new LinearLayoutManager(this));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            startActivity(new Intent(
                    this, PreferenciasActivity.class));
            return true;
        } else {
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Parser p = new Parser.Builder().build();
        p.onFinish(
                new OnTaskCompleted() {
                    @Override
                    public void onTaskCompleted(Channel channel) {
                        noticias = channel.getArticles();
                        runOnUiThread(
                                () -> {
                                    RssAdapter adapter = new RssAdapter(
                                            getApplicationContext(),
                                            noticias
                                    );
                                    conteudoRSS.setAdapter(adapter);
                                    setContentView(conteudoRSS);
                                }
                        );
                    }
                    @Override
                    public void onError(Exception e) {
                        Log.e("RSS_APP",e.getMessage());
                    }
                }
        );
        p.execute(urlFeed);
    }
    protected void onResume() {
        super.onResume();
    }

    private String getRssFeed(String feed) throws IOException {
        InputStream in = null;
        String rssFeed;
        try {
            URL url = new URL(feed);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            in = conn.getInputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            for (int count; (count = in.read(buffer)) != -1; ) {
                out.write(buffer, 0, count);
            }
            byte[] response = out.toByteArray();
            rssFeed = new String(response, StandardCharsets.UTF_8);
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return rssFeed;
    }
}

ItemRssViewHolder.java

package br.ufpe.cin.android.rss;

import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

public class ItemRssViewHolder extends RecyclerView.ViewHolder {
    TextView titulo = null;
    ImageView image = null;
    TextView data = null;
    TextView url = null;

    public ItemRssViewHolder(View itemCard) {
        super(itemCard);
        this.titulo = itemCard.findViewById(R.id.titulo);
        this.image = itemCard.findViewById(R.id.imagem);
        this.data = itemCard.findViewById(R.id.dataPublicacao);
        this.url = itemCard.findViewById(R.id.url);
        itemCard.setOnClickListener(
                v -> {
                    String uri = url.getText().toString();
                    Intent intent = new Intent(Intent.ACTION_VIEW,
                            Uri.parse(uri));
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    itemCard.getContext().startActivity(intent);

                }
        );
    }
}

RssAdapter.java

package br.ufpe.cin.android.rss;

import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.prof.rssparser.Article;
import com.squareup.picasso.Picasso;

import java.util.List;

import static br.ufpe.cin.android.rss.R.layout.linha;

public class RssAdapter extends RecyclerView.Adapter <ItemRssViewHolder> {

    List<Article> noticias;
    Context context;

    public RssAdapter(Context c, List<Article> noticias) {
        this.noticias = noticias;
        this.context = c;
    }

    public int getCount() {
        return noticias.size();
    }

    public Object getItem(int i) {
        return noticias.get(i);
    }

    @NonNull
    @Override
    public ItemRssViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(R.layout.linha, parent, false);
        ItemRssViewHolder viewHolder = new ItemRssViewHolder(v);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ItemRssViewHolder viewHolder, int i) {
        Article noticia = noticias.get(i);
        viewHolder.titulo.setText(noticia.getTitle());
        viewHolder.data.setText("Publicado em: " + noticia.getPubDate().substring(0, 22));
        viewHolder.url.setText(noticia.getLink());
        String url = noticias.get(i).getImage();
        Picasso.with(context)
                .load(url)
                .into(viewHolder.image);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public int getItemCount() {
        return noticias.size();
    }
}

因为您将 Recyclerview 的顶部与父级 (ConstraintLayout) 的顶部对齐,所以它覆盖了工具栏。

app:layout_constraintTop_toTopOf="parent" 替换为 app:layout_constraintTop_toBottomOf="@id/toolbar" 以将 Recyclerview 的顶部与工具栏的底部对齐

将这些属性添加到您的工具栏:

  • app:layout_constraintBottom_toTopOf="@id/conteudoRSS"
  • app:layout_constraintTop_toTopOf="parent"
  • app:layout_constraintStart_toStartOf="parent"
  • app:layout_constraintEnd_toEndOf="parent"
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/conteudoRSS"
        tools:ignore="MissingConstraints">
    </androidx.appcompat.widget.Toolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/conteudoRSS"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar"/> <!-- replace app:layout_constraintTop_toTopOf="parent"  -->

</androidx.constraintlayout.widget.ConstraintLayout>

LinearLayout 也不错,在这种情况下它比 ConstraintLayout 更简单

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize">
    </androidx.appcompat.widget.Toolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/conteudoRSS"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>

试试这个

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@id/conteudoRSS"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/toolbar"
        app:layout_constraintStart_toStartOf="parent">
    </androidx.appcompat.widget.Toolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/conteudoRSS"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar" /><!--TODO:Edit-->
</androidx.constraintlayout.widget.ConstraintLayout>

如果这不起作用,请尝试将 android:layout_marginTop="?attr/actionBarSize" 添加到 RecyclerView

希望这对您有所帮助。随时要求澄清...

问题在 Vishnu 的帮助下得到解决,他回答了这个问题。 问题似乎是我在主 activity 上创建了一个新的回收视图,而不是使用布局 xml 文件定义的回收视图。 要解决这个问题,只需要从主 activity:

中删除两行
conteudoRSS = new RecyclerView(this);
setContentView(conteudoRSS);

并且只维护recycleview的第一个定义,如下:

RecyclerView conteudoRSS;
conteudoRSS = findViewById(R.id.conteudoRSS);