hackerman
hackerman

Reputation: 1291

Views not properly contained within single list item

My goal is to pull three JSON Objects from an adapter and display them as three text views in one list item. It should look like this when only one set of objects in present: enter image description here

Instead it's showing this:

enter image description here

I can't see anything wrong in my XML or adapter. (Shown Below) What can cause the textviews to split into more list items than desired like this?

list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:padding="2dp"
    android:layout_height="match_parent">


    <RelativeLayout
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:id="@+id/outer"
        android:padding="3dp"
        android:background="#08445e"
        android:layout_toLeftOf="@+id/button"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

        <TextView
            android:id="@+id/textViewRoom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Room "
            android:textColor="#fff"
            android:layout_alignParentLeft="true"/>

        <TextView
            android:id="@+id/textViewTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Time: "
            android:layout_alignParentRight="true"
            android:textColor="#fff" />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:id="@+id/inner"
        android:background="#d1d1d1"
        android:layout_below="@+id/outer"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_toLeftOf="@+id/button"
        android:layout_toStartOf="@+id/button">

        <TextView
            android:id="@+id/textViewRequest"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:textColor="#000"
            android:textSize="20sp"
            android:layout_centerHorizontal="true" />

    </RelativeLayout>

    <Button
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="#dc2f2f"
        android:id="@+id/button"
        android:text="Mark Done"
        android:textColor="#fff"
        android:textAllCaps="false"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignBottom="@+id/inner"
        android:layout_alignParentTop="true" />
</RelativeLayout>

MainAdapter.Java:

package com.example.duncan.recyclerviewproject;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;



public class MainAdapter extends BaseAdapter {
    SharedPreferences preferences;

    Context context;
    ArrayList<String> arraylist=new ArrayList<>();

    public MainAdapter(Context context,
                       ArrayList<String>arrayList){
        this.context=context;
        this.arraylist=arrayList;
    }


    @Override
    public int getCount() {
        return arraylist.size();
    }

    @Override
    public Object getItem(int i) {
        return i;
    }

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

    public class ViewHolder{
        Button   mark_btn;
        public TextView textViewRoom;
        public TextView textViewRequest;
        public TextView textViewTime;
    }

    @Override
    public View getView(final int position, View row, ViewGroup parent) {
        View convertView=row;
       final ViewHolder holder;
        if(convertView==null) {

            holder=new ViewHolder();
            LayoutInflater infalInflater = (LayoutInflater)
                    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.custom_layout, null);

            holder.mark_btn= (Button) convertView.findViewById(R.id.button);
            holder.textViewRoom = (TextView) 
convertView.findViewById(R.id.textViewRoom);
            holder.textViewRequest = (TextView) 
convertView.findViewById(R.id.textViewRequest);
            holder.textViewTime = (TextView) 
convertView.findViewById(R.id.textViewTime);
            convertView.setTag(holder);

        }else{
            holder = (ViewHolder) convertView.getTag();

        }


        holder.textViewRoom.setText(arraylist.get(position));
        holder.textViewRequest.setText(arraylist.get(position));
        holder.textViewTime.setText(arraylist.get(position));

        holder.mark_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "Marked complete", 
Toast.LENGTH_SHORT).show();
            }
        });


        return convertView;
    }

    }

MainActivity.java

package com.example.duncan.recyclerviewproject;

import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

import me.anwarshahriar.calligrapher.Calligrapher;

public class MainActivity extends AppCompatActivity {


    private static final String URL_DATA = 
"https://summerproject17.herokuapp.com/api/request/?format=json";
    private ListView recyclerView;
    private MainAdapter adapter;

    private ArrayList<String> listItems;

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

        recyclerView = (ListView) findViewById(R.id.list);
       /* recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));*/
//is the below line newly added?
        listItems = new ArrayList<>();

        loadRecyclerViewData();

        Calligrapher calligrapher = new Calligrapher(this);
        calligrapher.setFont(this, "elegantluxpro-mager.ttf", true);

    }


    private void loadRecyclerViewData(){
        StringRequest stringRequest = new StringRequest(Request.Method.GET,
                URL_DATA,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            JSONArray array = 
jsonObject.getJSONArray("results");

                            for(int i = 0; i < array.length(); i++){
                                JSONObject o = array.getJSONObject(i);
                              /*  ListItem item = new ListItem(
                                        o.getString("room"),
                                        o.getString("request"),
                                        o.getString("unixTime")
                                );*/
                                listItems.add(o.getString("room"));
                                listItems.add(o.getString("request"));
                                listItems.add(o.getString("room"));
                            }

                            Log.e("size",""+listItems.size());

                            adapter = new 
MainAdapter(MainActivity.this,listItems);
                            recyclerView.setAdapter((ListAdapter) adapter);

                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("VOLLEY", error.getMessage());
                    }
                });
        RequestQueue requestQueue = Volley.newRequestQueue(this);
        requestQueue.add(stringRequest);
    }
}

activity_main.xml list view portion:

<ListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/list"
    android:layout_below="@+id/relativeLayout2">

</ListView>

Note: I asked a similar question a week or so back but this is different in that the structure of the list items has changed and more details have been provided in this question.

Upvotes: 1

Views: 58

Answers (2)

adamarla
adamarla

Reputation: 27

My goal is to pull 3 JSON Objects from an adapter and display them as 3 text views in one list item. It should look like this when only one set of objects in present:

The list adapter is designed to have a 1 to 1 map with the list that is backing it. Therefore your approach above is somewhat flawed as I understand it. If you want three JSON Objects to be displayed as the three TextViews of a single list item, you need to translate to another List comprised of (for example) the class ViewHolder which you defined. Look up ListView using custom adapter for this. Not sure why ViewHolder is an internal class, it can simply be a non-public class in the same file.

I can't see anything wrong in my XML

There appear to be some redundancies in your xml layout. What you could do is define a custom layout for your list item, which would have a single RelativeLayout that has all the three text views and the button (something like this).

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/list_item"
    android:padding="3dp">

    <TextView
        android:id="@+id/textViewRoom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Room "
        android:textColor="#fff"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"/>

    <Button
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="#dc2f2f"
        android:id="@+id/button"
        android:text="Mark Done"
        android:textColor="#fff"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />

    <TextView
        android:id="@+id/textViewTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Time: "
        android:layout_alignParentTop="true"
        android:alignLeft="@id/button"
        android:textColor="#fff" />

    <TextView
        android:id="@+id/textViewRequest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:textColor="#000"
        android:textSize="20sp"
        android:alignBottom="@id/textViewRoom"
        android:alignLeft="@id/button"
        android:layout_centerHorizontal="true" />


</RelativeLayout>

Or you could use a combination of LinearLayout and RelativeLayout. But there is no orientation attribute in RelativeLayout, so you need to get rid of that as well.

Not sure if this is sufficient to solve the specific problem you were struggling with but I hope it helps.

Upvotes: 2

Gary99
Gary99

Reputation: 1770

As adamarta says, the there's a 1 to 1 mapping between your list & what's displayed. 1 item in your ArrayList gives you 1 item in your ListView. A common way of doing this is creating a data model class. It would look like this.

public class RoomItem {
    private String room;
    private String time;
    private String request;

    public RoomItem(String room, String request, String time) {
        this.room = room;
        this.request = request;
        this.time = time;
    }

    public RoomItem() {        
    }

    public String getRoom() {
        return room;
    }

    public void setRoom(String room) {
        this.room = room;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public String getRequest() {
        return request;
    }

    public void setRequest(String request) {
        this.request = request;
    }
}

Your ArrayList declaration would change like this.

    private ArrayList<RoomItem> listItems;

And you'll have to change the ArrayList uses in your adapter as well.

Also, in getView() in the adapter, set the values like this

    holder.textViewRoom.setText(arraylist.get(position).getRoom());
    holder.textViewRequest.setText(arraylist.get(position).getRequest());
    holder.textViewTime.setText(arraylist.get(position).getTime());

In MainActivity, in loadRecyclerViewData(), replace your 3 current listItems.add(...) with this.

    // create a new data item with the new information
    RoomItem item = new RoomItem(o.getString("room"),
            o.getString("request"),
            o.getString("room"));)

    // then add that item to the list 
    listItems.add(item);

You could also create an empty item then add each field (do either this or the above code, not both)

    RoomItem item = new RoomItem();
    item.setRoom(o.getString("room");
    etc.

As for your layout, if it looks OK in the preview, then it's probably OK. Except... you're replacing the labels (Room & Time) with the values. I don't think you want to do that. I think this is what you need.

    <TextView
        android:id="@+id/textViewRoomLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Room: "
        android:textColor="#fff"
        android:layout_alignParentLeft="true"/>

    <TextView
        android:id="@+id/textViewRoom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="placeholder"
        android:textColor="#fff"
        android:layout_toEndOf="@+id/textViewRoomLabel"
        android:layout_marginStart="10dp"
        />

    <TextView
        android:id="@+id/textViewTimeLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Time: "
        android:layout_toStartOf="@+id/textViewTime"
        android:layout_marginEnd="10dp"
        android:textColor="#fff" />

    <TextView
        android:id="@+id/textViewTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="placeholder"
        android:layout_alignParentRight="true"
        android:textColor="#fff" />

I haven't built and tested this so I may have missed a couple of small things like needing to make changes for the new ArrayList etc but I think this is what you need.

Upvotes: 2

Related Questions