kanytu / android-parallax-recyclerview

An adapter which could be used to achieve a parallax effect on RecyclerView.
Apache License 2.0
1.61k stars 284 forks source link

Add a custom row between header image & recyclerveiw #19

Closed aniruddhasm closed 9 years ago

aniruddhasm commented 9 years ago

hello,

Thankyou for the library. It's working fine. The thing I want to achieve is add a custom row or relative layout between header image & recyclerview. I have tried following this example http://stackoverflow.com/questions/26568087/parallax-header-effect-with-recyclerview#text. The example where the guy inserted a text " Hi, I'm new here " before test 1 & after header image.

The problem with my code is the first element of recylcerview is getting replaced by the custom row that I have created. This is the output http://s11.postimg.org/w31cmp3qb/detail.jpg . the "mobileapp" is the data in the recyclerview .mobileapp 0 is getting hidden or replaced by " MobileApp 0".

This is my code I am using

adapter.implementRecyclerAdapterMethods(new ParallaxRecyclerAdapter.RecyclerAdapterMethods() { @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { if (i == 0) { ((ViewHolder) viewHolder).title.setText(“MobileApp” + i); ((ViewHolder) viewHolder).ct.setText(“test”); } else if (i > 0) { ((ViewHolder) viewHolder).title2.setText((“mobileapp” + i); ((ViewHolder) viewHolder). title3.setText(“”); }

        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(
                ViewGroup viewGroup, final int i) {
            final ViewHolder holder;
            if (i == 3) {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recylceview_firstcard, viewGroup,
                        false), i);

            } else {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recyclerview, viewGroup, false), i);
                                }
            return holder;
        }

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

});

please help

kanytu commented 9 years ago

Hi. Your onCreateViewHolder is correct. You're using i == 3 which is the first view and it's correct.

The problem is on onBindViewHolder. You can't rely on that i==0 because the header is the 0.

You can either use 1 instead of 0 or create a custom value on your ViewHolder class that defines if you should display the "MobileApp" or the "mobileapp".

Resuming: Create a value on your ViewHolder class, let's say a boolean field. Add it to your ViewHolder constructor. Set false or true depending on the ViewType returned on onCreateViewHolder and on the other side get the value and populate the view the way you want..

Hope it helps

aniruddhasm commented 9 years ago

Thanks for quick reply.

Can you please help me with small code snipet as I am new to recylcerview so I am not able to figure out as to what exactly you want me to change. please help.

kanytu commented 9 years ago

Try using: (replace 0 by 1 because position 0 is the header)

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder,
    int i) {
    if (i == 1) {
        ((ViewHolder) viewHolder).title.setText(“MobileApp” + i);
        ((ViewHolder) viewHolder).ct.setText(“test”);
    } else if (i > 1) {
        ((ViewHolder) viewHolder).title2.setText((“mobileapp” + i);
        ((ViewHolder) viewHolder). title3.setText(“”);
}
aniruddhasm commented 9 years ago

Thankyou for quick reply.

But my app crashes with I try replacing the onBindViewHolder code with yours i.e replace 0 with 1.

I am getting this error

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference

at
" ((ViewHolder) viewHolder).title.setText(“MobileApp” + i); "

please help

kanytu commented 9 years ago

That's because there is no view with that Id in that position. Keep in mind that in position == 1 you're inflating R.layout.row_recylceview_firstcard instead of R.layout.row_recyclerview.

aniruddhasm commented 9 years ago
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder,
    int i) {
    if (i == 0) {
        ((ViewHolder) viewHolder).title.setText(“MobileApp” + i);
        ((ViewHolder) viewHolder).ct.setText(“test”);
    } else if (i > 0) {
        ((ViewHolder) viewHolder).title2.setText((“mobileapp” + i);
        ((ViewHolder) viewHolder). title3.setText(“”);
}

 @Override
        public RecyclerView.ViewHolder onCreateViewHolder(
                ViewGroup viewGroup, final int i) {
            final ViewHolder holder;
            if (i == 3) {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recylceview_firstcard, viewGroup,
                        false), i);

            } else {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recyclerview, viewGroup, false), i);
                                }
            return holder;
        }

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

static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView title;
        public TextView title3;
        public TextView title2;
        public TextView ct;
        public ImageView shareImage;

        public ViewHolder(View itemView, int viewType) {
            super(itemView);
            if (viewType == 3) {
                title = (TextView) itemView.findViewById(R.id.title);
                ct= (TextView) itemView
                        .findViewById(R.id.ct);
            } else {
                title2= (TextView) itemView.findViewById(R.id.title2);
                title3 = (TextView) itemView
                        .findViewById(R.id.title3);
                shareImage = (ImageView) itemView.findViewById(R.id.shareImage);
            }
        }
    }

    public int getItemViewType(int position) {
        int viewType = 1; // Default is 1
        if (position == 0)
            viewType = 0; // if zero, it will be a header view
        return viewType;
    }

This is my code.

please help

kanytu commented 9 years ago

Remove getItemViewType method from there. Next I said "replace 0 by 1 because position 0 is the header"... You still have 0 on onBindViewHolder.

aniruddhasm commented 9 years ago
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder,
    int i) {
    if (i == 1) {
        ((ViewHolder) viewHolder).title.setText(“MobileApp” + i);
        ((ViewHolder) viewHolder).ct.setText(“test”);
    } else if (i > 1) {
        ((ViewHolder) viewHolder).title2.setText((“mobileapp” + i);
        ((ViewHolder) viewHolder). title3.setText(“”);
}

 @Override
        public RecyclerView.ViewHolder onCreateViewHolder(
                ViewGroup viewGroup, final int i) {
            final ViewHolder holder;
            if (i == 3) {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recylceview_firstcard, viewGroup,
                        false), i);

            } else {
                holder = new ViewHolder(getLayoutInflater().inflate(
                        R.layout.row_recyclerview, viewGroup, false), i);
                                }
            return holder;
        }

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

static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView title;
        public TextView title3;
        public TextView title2;
        public TextView ct;
        public ImageView shareImage;

        public ViewHolder(View itemView, int viewType) {
            super(itemView);
            if (viewType == 3) {
                title = (TextView) itemView.findViewById(R.id.title);
                ct= (TextView) itemView
                        .findViewById(R.id.ct);
            } else {
                title2= (TextView) itemView.findViewById(R.id.title2);
                title3 = (TextView) itemView
                        .findViewById(R.id.title3);
                shareImage = (ImageView) itemView.findViewById(R.id.shareImage);
            }
        }
    }

I replaced the code to whatever you said but still it is crashing

05-29 01:37:06.151: E/AndroidRuntime(3156): java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
05-29 01:37:06.151: E/AndroidRuntime(3156):     at abc.android.com.RowActivity$5.onBindViewHolder(RowActivity.java:319)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at abc.android.com.helper.ParallaxRecyclerAdapter.onBindViewHolder(ParallaxRecyclerAdapter.java:117)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:4048)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3366)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:522)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.View.layout(View.java:15596)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewGroup.layout(ViewGroup.java:4966)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2072)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1829)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.Choreographer.doCallbacks(Choreographer.java:580)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.Choreographer.doFrame(Choreographer.java:550)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.os.Handler.handleCallback(Handler.java:739)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.os.Handler.dispatchMessage(Handler.java:95)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.os.Looper.loop(Looper.java:135)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at android.app.ActivityThread.main(ActivityThread.java:5221)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at java.lang.reflect.Method.invoke(Native Method)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at java.lang.reflect.Method.invoke(Method.java:372)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
05-29 01:37:06.151: E/AndroidRuntime(3156):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Above is the error log

Please help

kanytu commented 9 years ago

God I'm sorry. I haven't look at the code in a long time. 0 is correct. The adapter changes the math for you and you have to use 0. If you use 0 instead what is the result?

aniruddhasm commented 9 years ago

Thanks,

when I am using 0 this is the output

http://s11.postimg.org/w31cmp3qb/detail.jpg

"MobileApp 0" is replacing "mobileapp 0"

What I want to achieve is "MobileApp 0" then "mobileapp 0".. "mobileapp 1" & so on.

Please help

kanytu commented 9 years ago

Ohhhhh. That's easier than I through.

Just replace i by i-1 on:

} else if (i > 0) { ((ViewHolder) viewHolder).title2.setText((“mobileapp” + i); ((ViewHolder) viewHolder). title3.setText(“”); }

So it will look like this: ((“mobileapp” + i-1)

aniruddhasm commented 9 years ago

Thanks,

But I have 10 rows & using your logic when I do ( i-1 ) then I'll end up showing till "mobileapp 9" which actually should end up at " mobileapp 10 ".

kanytu commented 9 years ago

So what you need to do is to add an item at position 0 which is a copy of the object in position 0 before you call setAdapter.

Like this:

List<Object> objects = new ArrayAdapter<Object>();
//...... add your objects here
objects.add(0,objects.get(0));
    adapter = new Adapter(objects);
recyclerview.setAdapter(adapter);

This way you will add a copy of the first item (which you want to be a title)

aniruddhasm commented 9 years ago

Thanks,

This is how I tried doing it

final ParallaxRecyclerAdapter<String> adapter = new ParallaxRecyclerAdapter<>(content);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        View header = getLayoutInflater().inflate(R.layout.header, recyclerView, false);
        adapter.setParallaxHeader(header, recyclerView);
        adapter.setData(content);
        adapter.implementRecyclerAdapterMethods(new ParallaxRecyclerAdapter.RecyclerAdapterMethods() {
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
              // my code which I submitted previously
            }

            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
                final ViewHolder holder = new ViewHolder(getLayoutInflater().inflate(R.layout.row_recyclerview, viewGroup, false));
               // my code which I submitted previously
            }

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

// your code

List<Object> objects = new ArrayAdapter<Object>();
//...... add your objects here
objects.add(0,objects.get(0));
    adapter = new Adapter(objects);
        recyclerView.setAdapter(adapter);

I am not getting what is objects can you help me with the code snippet with my data. please help

kanytu commented 9 years ago

The object is whatever you're using to fill your adapter...... I think you lack some android programming knowledge. Before you proceed I suggest you learn more.

For a normal programmer this should be enough to fix the problem:

Add an item to your list that's a copy of the first one. If you're passing "content" to your adapter then you need to add another object to "content" list. Simple as that.

aniruddhasm commented 9 years ago

Thank you sir for your valuable time. I've solved by problem using

objects.add(0,objects.get(0));