Skip to content

Latest commit

 

History

History
663 lines (479 loc) · 21.5 KB

CARD.md

File metadata and controls

663 lines (479 loc) · 21.5 KB

Cards Library: Card

In this page you can find info about:

Creating a base Card

Creating a Card is pretty simple.

First, you need an XML layout that will display the Card.

        <it.gmariotti.cardslib.library.view.CardViewNative
            android:id="@+id/carddemo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="12dp"
            android:layout_marginTop="12dp"/>

Then create a model:

      //Create a Card
      Card card = new Card(getContext());

      //Create a CardHeader
      CardHeader header = new CardHeader(getContext());
      ....
      //Add Header to card
      card.addCardHeader(header);

Last get a reference to the CardView from your code, and set your `Card.

       //Set card in the cardView
       CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo);

       cardView.setCard(card);

Use your custom card-layout

Card Library provides 2 built-in card layouts.

For the CardViewNative:

For the CardView:

You can easily build your layout.

The quickest way to start with this would be to copy one of this files and create your layout.

Then you can inflate your layout in the CardViewNative using the attr: card:card_layout_resourceID="@layout/my_layout

Example:

        <it.gmariotti.cardslib.library.view.CardViewNative
            android:id="@+id/carddemo_thumb_url"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="12dp"
            card:card_layout_resourceID="@layout/native_card_thumbnail_layout"
            android:layout_marginTop="12dp"/>

If you want to preserve built-in features you have to use the same IDs for Compound Views (Header, Shadow, Thumbnail) and Built-in Frame (Content, Expand).

Each component (view or frame) can be easily customize with style/drawable resources or inflating custom inner layout.(See doc about Header,Shadow,Thumbnail,Expand).

Use your content inner layout

Each Card has a content Frame area, where you can display your custom info.

The built-in content inner layout is quite useless, and provides a simple title.

      //Create a Card
      Card card = new Card(getContext());

      //Set the card inner text
      card.setTitle("My Title");


      //Set card in the cardView
      CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo_card_id);
      cardView.setCard(card);

Screen

Surely you have to custom this area.

To do this you can use the Card constructor

        //Create a Card
        Card card = new Card(getContext(),R.layout.carddemo_example_inner_content);

        //Set the card inner text
        card.setTitle(getString(R.string.demo_card_basetitle));

        //Set card in the cardView
        CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo_card_id);
        cardView.setCard(card);

This is the layout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

        <ImageView
            android:layout_marginTop="2dp"
            android:id="@+id/colorBorder"
            android:background="@drawable/rectangle"
            android:layout_width="10dp"
            android:layout_height="@dimen/card_base_empty_height"/>

        <!-- Use same ID to use built-in features -->
        <TextView
            android:layout_toRightOf="@id/colorBorder"
            android:id="@+id/card_main_inner_simple_title"
            android:layout_marginLeft="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </RelativeLayout>

Screen

Extending Card class

The layout above is quite simple, and doesn't need to set values.

If you want to have a full control on the main content, you can extend Card class, inflate your custom inner layout and then set your values with setupInnerViewElements method.

    public class CustomCard extends Card {

        protected TextView mTitle;
        protected TextView mSecondaryTitle;
        protected RatingBar mRatingBar;

        /**
         * Constructor with a custom inner layout
         * @param context
         */
        public CustomCard(Context context) {
            this(context, R.layout.carddemo_mycard_inner_content);
        }

        /**
         *
         * @param context
         * @param innerLayout
         */
        public CustomCard(Context context, int innerLayout) {
            super(context, innerLayout);
            init();
        }

        /**
         * Init
         */
        private void init(){

            //No Header

            //Set a OnClickListener listener
            setOnClickListener(new OnCardClickListener() {
                @Override
                public void onClick(Card card, View view) {
                    Toast.makeText(getContext(), "Click Listener card=", Toast.LENGTH_LONG).show();
                }
            });
        }

        @Override
        public void setupInnerViewElements(ViewGroup parent, View view) {

            //Retrieve elements
            mTitle = (TextView) parent.findViewById(R.id.carddemo_myapps_main_inner_title);
            mSecondaryTitle = (TextView) parent.findViewById(R.id.carddemo_myapps_main_inner_secondaryTitle);
            mRatingBar = (RatingBar) parent.findViewById(R.id.carddemo_myapps_main_inner_ratingBar);


            if (mTitle!=null)
                mTitle.setText(R.string.demo_custom_card_google_maps);

            if (mSecondaryTitle!=null)
                mSecondaryTitle.setText(R.string.demo_custom_card_googleinc);

            if (mRatingBar!=null)
                mRatingBar.setNumStars(5);
                mRatingBar.setMax(5);
                mRatingBar.setRating(4.7f);

        }
    }

Then you can simply do:

        //Create a Card
        Card card = new CustomCard(getActivity());

        //Set card in the cardView
        CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo_example_card3);
        cardView.setCard(card);

Screen

Listeners

The Card class provides some listeners to handle callbacks.

  • OnSwipeListener : invoked when the card is swiped
  • OnCardClickListener : invoked when the card is clicked
  • OnLongCardClickListener : invoked when the card is long clicked
  • OnExpandAnimatorStartListener : invoked when the expand animation starts
  • OnCollapseAnimatorStartListener : invoked when the collapse animation starts
  • OnExpandAnimatorEndListener : invoked when the expand animation ends
  • OnCollapseAnimatorEndListener : invoked when the collapse animation ends

See Card Header: for Header listeners.

Card with a swipe action

If you want to enable the swipe action on a Card is very simple:

        //Create a Card
        Card card = new CustomCard(getActivity());

        //Enable a swipe action
        card.setSwipeable(true);

        //Set card in the cardView
        CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo_example_card3);
        cardView.setCard(card);

It is not mandatory. You can set a Card.OnSwipeListener to listen the swipe action.

        //You can set a SwipeListener.
        card.setOnSwipeListener(new Card.OnSwipeListener() {
            @Override
            public void onSwipe(Card card) {
                //Do something
            }
        });

Clickable card

If you want a clickable card, enable a Card.OnCardClickListener

        //Create a Card
        Card card = new CustomCard(getActivity());

        //Set onClick listener
        card.setOnClickListener(new Card.OnCardClickListener() {
            @Override
            public void onClick(Card card, View view) {
                Toast.makeText(getActivity(),"Clickable card", Toast.LENGTH_LONG).show();
            }
        });

        //Set card in the cardView
        CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.carddemo_example_card3);
        cardView.setCard(card);

Screen

ClickListener on a specific area

With OnCardClickListener you can set your card as clickable.

If you want to set only a part (or more parts) as clickable you can use this code:

        //Set a clickListener on ContentArea
        card1.addPartialOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW, new Card.OnCardClickListener() {
            @Override
            public void onClick(Card card, View view) {
                Toast.makeText(getActivity(),"Click on Content Area", Toast.LENGTH_LONG).show();
            }
        });

        //Set a clickListener on Header Area
        card2.addPartialOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW, new Card.OnCardClickListener() {
            @Override
            public void onClick(Card card, View view) {
                Toast.makeText(getActivity(),"Click on Header Area", Toast.LENGTH_LONG).show();
            }
        });

        //Set a clickListener on Thumbnail area
        card3.addPartialOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW, new Card.OnCardClickListener() {
            @Override
            public void onClick(Card card, View view) {
                Toast.makeText(getActivity(),"Click on Thumbnail", Toast.LENGTH_LONG).show();
            }
        });

Screen

Refresh a card

If you need to change same value in a card, you can use your Card model:

    card.setTitle("New title");

    //Change Header
    card.getCardHeader().setTitle("New image");

    //Change Thumbnail
    card.getCardThumbnail().setDrawableResource(R.drawable.ic_std_launcher);

    //Remove click listener
    card.setOnClickListener(null);
    card.setClickable(false);

and then call notifyDataSetChanged(); method on the Card:

    //Call refresh
    card.notifyDataSetChanged();

You can see the example in 'ChangeValueCardFragment'.

Replace inner layout in a card

If you need to replace the inner layout in a card, you can use your Card model and call replaceCard method on cardView:

    //Call replace
    card3.setInnerLayout(R.layout.carddemo_suggested_inner_content);
    cardView3 = (CardViewNative) getActivity().findViewById(R.id.carddemo_card_changevalue_id3);
    cardView3.replaceCard(card3);

In your setupInnerViewElements() method you should provide the correct way to put values in your ui elements according to the layout. You can see the example in ChangeValueCardFragment.

Customize Card background

For CardViewNative:

PAY ATTENTION:Don't use with CardViewNative

For CardView:

The quickest way to customize the card background would be to copy the styles/drawables in your project.

Default cards use res/drawable/card_selector.xml.

    <selector xmlns:android="http://schemas.android.com/apk/res/android"
              android:exitFadeDuration="@android:integer/config_mediumAnimTime">
        <item android:state_pressed="true" android:drawable="@drawable/pressed_background_card"/>
        <item android:drawable="@drawable/card_background"/>
    </selector>

In res/drawable/card_background.xml and res/drawable/pressed_background_card.xml you can easy customize the color and shape.

    <shape xmlns:android="http://schemas.android.com/apk/res/android"
           android:shape="rectangle">
        <solid android:color="@color/card_background"/>
        <corners android:radius="@dimen/card_background_default_radius"/>
    </shape>

If you want to change the color, the quickest way would be to customize this value in your project:

    <color name="card_background">#FFF</color>

If you want to change the rounded corners, the quickest way would be to customize this value in your project:

    <dimen name="card_background_default_radius">2dip</dimen>

Change dynamically Card background

If you need to change dynamically the card background you can use this code:

For CardViewNative:

PAY ATTENTION:Don't use with CardViewNative (not yet supported).

    ColorCard card = new ColorCard(this.getActivity());

    //Set Background resource
    card.setBackgroundColorResourceId(R.color.demo_card_background_color5);

Where R.color.demo_card_background_color5 is a color.

For CardView:

    ColorCard card = new ColorCard(this.getActivity());

    //Set Background resource
    card.setBackgroundResourceId(R.drawable.demo_card_selector_color1);

Where R.drawable.demo_card_selector_color1 can be a selector or a drawable.

Example:

    <selector xmlns:android="http://schemas.android.com/apk/res/android"
                         android:exitFadeDuration="@android:integer/config_mediumAnimTime">
        <item android:state_pressed="true" android:drawable="@drawable/pressed_background_card"/>
        <item android:drawable="@drawable/demo_card_background_color1"/>
    </selector>

You can see this example: Colored cards example (source).

Screen

Change dynamically Card background with a Drawable object

Only for CardView.

Also you can customize your background using a Drawable object:

     StateListDrawable newDrawable = new StateListDrawable();
     newDrawable.addState(new int[]{android.R.attr.state_pressed},
            getResources().getDrawable(R.drawable.pressed_background_card));
     newDrawable.addState(new int[] {}, getResources().getDrawable(R.drawable.demo_card_background_color2));

     card4setBackgroundResource(newDrawable);

You can see this example: (source).

Export card as bitmap

You can export your Card as a Bitmap.

It is very simple.

    CardViewNative cardView = (CardViewNative) getActivity().findViewById(R.id.myCard);
    Bitmap bitmap = cardView.createBitmap();

Screen

You can use this bitmap for your scopes.

In BitmapUtils class there are some built-in methods:

  1. You can use File photofile= BitmapUtils.createFileFromBitmap(bitmap) to save the image in Environment.DIRECTORY_PICTURES folder.
  2. You can use Intent intent = BitmapUtils.createIntentFromImage(photofile) to put the image as EXTRA_STREAM in a Intent.

You can see the example in BirthDayCardFragment and StockCardFragment where you can share the card as a bitmap.

Using Card with contextual action mode

PAY ATTENTION:Don't use with CardViewNative and/or Toolbar (not yet supported)

If you would like to use a card with you can use a code like this:

        ActionMode mActionMode;

        //Create a Card
        Card mCardCab = new Card(getActivity());

        //Set the card inner text
        mCardCab.setTitle(getString(R.string.demo_card_basetitle));

        //Set onClick listener
        mCardCab.setOnLongClickListener(new Card.OnLongCardClickListener() {
            @Override
            public boolean onLongClick(Card card, View view) {
                if (mActionMode != null) {
                    view.setActivated(false);
                    mActionMode.finish();
                    return false;
                }
                // Start the CAB using the ActionMode.Callback defined below
                mActionMode = getActivity().startActionMode(mActionModeCallback);
                view.setActivated(true);
                return true;
            }
        });

        //Set card in the cardView
        cardViewCab = (CardView) getActivity().findViewById(R.id.carddemo_example_card_cab);
        cardViewCab.setCard(mCardCab);
    }


    private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {

        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.carddemo_cab_example, menu);
            return true;
        }

        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false; // Return false if nothing is done
        }

        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.carddemo_toast:
                    Toast.makeText(getActivity(), "Change Text",
                            Toast.LENGTH_LONG).show();
                    if (mCardCab!=null && cardViewCab!=null){
                        //Example to change dinamically your card
                        mCardCab.setTitle(getString(R.string.demo_title_cab2));
                        cardViewCab.refreshCard(mCardCab);
                    }
                    mode.finish(); // Action picked, so close the CAB
                    return true;
                default:
                    return false;
            }
        }

        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mActionMode = null;
            if (mCardCab!=null)
                cardViewCab.setActivated(false);
        }
    };

You can see this example: (source).

Using a ForegroundLinearLayout

This paragraph is only for CardView. The default layout used by the CardViewNative is a ForegroundLinearLayout.

You can draw the stateful drawable on top, using a foreground selector.

To achieve this behaviour you have to do these steps:

  • change the android:id="@+id/card_main_layout" element in your card layout

Default layout:

    <!-- Standard Card visible layout -->
    <LinearLayout
        android:id="@+id/card_main_layout"
        style="@style/card.main_layout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

Foreground layout:

    <!-- Foreground Card visible layout -->
    <it.gmariotti.cardslib.library.view.ForegroundLinearLayout
        android:id="@+id/card_main_layout"
        style="@style/card.main_layout_foreground"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
  • change the default style with something like the style="@style/card.main_layout_foreground"

This style uses a foreground selector using the android:foreground attribute, and a simple drawable(or color) for background.

    <!-- Style for Main Layout with foreground selector-->
    <style name="card.main_layout_foreground">
        <item name="android:background">@drawable/card_background</item>
        <item name="android:foreground">@drawable/card_foreground_selector</item>
    </style>

You can see this example: (source).

Style

Pay attention. The default card has a min height.

Check this style:

 <style name="card.content_outer_layout">

You can override this value in your dimens.xml:

    <dimen name="card_base_empty_height">96dp</dimen>