parse.com - Nested query and Join Table

2019-09-09 01:37发布

问题:

I have problem with nested query. In query5.whereEqualTo("piwo", followList2.get(0)) I want to get the object, but it spits error that followList2 needs to be declared final, but when it does all the anonymous class gets in red with Cannot resolve constructor(...) error. Anyone got this before?

ParseQuery<ParseObject> query3 = ParseQuery.getQuery("Piwo");
                    query3.whereEqualTo("marka", beer); // TODO if(beer == "all") then don't use it
                    query3.findInBackground(new FindCallback<ParseObject>() {
                        public void done(List<ParseObject> followList2, ParseException e) {

                            if (followList2 != null) {
                                Log.d("ASD", "Szukane piwo: " + followList2.get(0).getString("marka"));
                            } else {
                                Log.d("ASD", "Zero wyników1");
                            }

                            ParseQueryAdapter<ParseObject> adapter =
                                    new ParseQueryAdapter<ParseObject>(this, new ParseQueryAdapter.QueryFactory<ParseObject>() {
                                        public ParseQuery<ParseObject> create() {
                                            // Here we can configure a ParseQuery to our heart's desire.
                                            ParseQuery query5 = new ParseQuery("Cena");
                                            query5.whereContainedIn("lokal", list);
                                            query5.whereEqualTo("piwo", followList2.get(0);
                                            query5.include("piwo");
                                            query5.include("lokal");
                                            query5.orderByAscending("cena");
                                            return query5;
                                        }
                                    });
                            adapter.setTextKey("lokal.place");
                            adapter.setImageKey("photo");

                            ListView listView = (ListView) findViewById(R.id.listview);
                            listView.setAdapter(adapter);

回答1:

If I understand you correctly, you already tried:

...
public void done(final List<ParseObject> followList2, ParseException e) {
...

Which for some reason makes the compiler unhappy.

I think there might be two possible solutions

  1. If you plan to use the followList2 objects elsewhere in your Activity/Fragment. Then simply declare a field variable to hold the result and read from that instead. This way the anonymous inner class should have access to it.
  2. Write followList2 to another local variable that is declared final. This way we are not altering the signature of the done() callback.

Solution 1:

List<ParseObject> mFollowList2; // field variable outside method
...
public void done(List<ParseObject> followList2, ParseException e) {
    mFollowList2 = followList2; 
    // use mFollowList2 in rest of code
...    

Solution 2:

public void done(List<ParseObject> followList2, ParseException e) {
    final List<ParseObject> finalFollowList2 = followList2; 
    // use finalFollowList2 in rest of code
...    

As said in the comments, I cannot recall having had the same problem but I hope this addresses the issue.

A third suggestion would be to try out Bolts https://github.com/BoltsFramework/Bolts-Android (comes with the parse API). In case you are familiar with promises in javascript, Bolts is essentially the same for Java. It removes the need for nesting calls, making a pyramid shaped chunk of code as the amount of depending queries grows. However, it does take some time to get used to, and in the simple cases it is not necessary.

Bonus:

As you have trouble using the text from your include in your adapter, I will show you some of my code as an example.

First, I have a simple layout for an item: res/layout/view_adapter_item_simple.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="?android:attr/activatedBackgroundIndicator"
    android:paddingTop="5dp">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:text="item" />


</RelativeLayout>

Next, here is my custom adapter:

public class SimpleParseAdapter<T extends ParseObject> extends
        ParseQueryAdapter<T> {

    private static final String TAG = SimpleParseAdapter.class.getSimpleName();

    private final String textCol;

    public SimpleParseAdapter(Context context, String textCol,
            QueryFactory<T> queryFactory) {
        super(context, queryFactory);
        this.textCol = textCol;
    }

    TextView text;

    @Override
    public View getItemView(T object, View v, ViewGroup parent) {

        if (v == null) {
            v = View.inflate(getContext(), R.layout.view_adapter_item_simple,
                    null);
        }

        super.getItemView(object, v, parent);

        text = (TextView) v.findViewById(R.id.text);

        text.setText(object.getString(textCol));

        return v;

    }


}

NOTICE: We are still not quite there yet. This works similar to the standard ParseQueryAdapter in that it only looks at the columns of the current class using text.setText(object.getString(textCol)).

However, one could fairly easy write a special purpose adapter to handle the nested include, for example:

public class SimpleParseIncludeAdapter<T extends ParseObject> extends
        ParseQueryAdapter<T> {

    private static final String TAG = SimpleParseIncludeAdapter.class.getSimpleName();

    private final String includeCol;
    private final String textCol;

    public SimpleParseIncludeAdapter(Context context, String includeCol, String textCol,
            QueryFactory<T> queryFactory) {
        super(context, queryFactory);
        this.includeCol = includeCol;
        this.textCol = textCol;
    }

    TextView text;

    @Override
    public View getItemView(T object, View v, ViewGroup parent) {

        if (v == null) {
            v = View.inflate(getContext(), R.layout.view_adapter_item_simple,
                    null);
        }

        super.getItemView(object, v, parent);

        text = (TextView) v.findViewById(R.id.text);

        text.setText(object.getParseObject(includeCol).getString(textCol));

        return v;

    }


}

Now using the adapter like this:

new SimpleParseIncludeAdapter(**context**, "lokal", "place",**queryFactory**); 

Where the queryFactory is obligated to do query.include("lokal") (includes the whole 'lokal' pointer), or `query.include("lokal.place") (only include the 'place' column of 'lokal');

Extra bonus - subclassing

As a final note, it does not look like you are using subclassing, but if you did, you could also have a specialized custom adapter for Cena subclasses.

public class CenaParseAdapter extends
        ParseQueryAdapter<Cena> {

    private static final String TAG = CenaParseAdapter.class.getSimpleName();



    public CenaParseAdapter(Context context, 
            QueryFactory<Cena> queryFactory) {
        super(context, queryFactory);

    }

    TextView text;

    @Override
    public View getItemView(Cena cena, View v, ViewGroup parent) {

        if (v == null) {
            v = View.inflate(getContext(), R.layout.view_adapter_item_simple,
                    null);
        }

        super.getItemView(object, v, parent);

        text = (TextView) v.findViewById(R.id.text);

        text.setText(cena.getPlace());

        return v;

    }


}

In this case cena.getPlace() could look up the included lokal:

// inside Cena sublass

public Lokal getLokal() { // assuming Lokal also is subclassed
    return (Lokal)getParseObject("lokal");
}

public String getPlace() {
    return (getLokal() != null) ? getLokal().getPlace() : "";
}

// inside Lokal subclass

public String getPlace() {
    return getString("place");
}