精华内容
下载资源
问答
  • 2021-06-11 18:09:01

    直接搬代码过来了

    	//这里写在前面
    	@InjectView(R.id.fl_frame)
        FrameLayout mFrame;
        @InjectView(R.id.tv_top)
        TextView tvLoginTop;
        private List<Fragment> fragments;
        private OnlineFragment onlineFragment;
        private OfflineFragment offlineFragment;
        private FragmentManager manager;
        		//这里写在onCreate
        		manager = getSupportFragmentManager();
        		//bundle1可以给fragment传值
        		Bundle bundle1 = new Bundle();
        		onlineFragment = new OnlineFragment();
       			offlineFragment = new OfflineFragment();
                onlineFragment.setArguments(bundle1);
                offlineFragment.setArguments(bundle1);
                fragments = new ArrayList<Fragment>();
            	fragments.add(onlineFragment);
            	fragments.add(offlineFragment);
            	//添加tab文字
            	ArrayList<String> titles = new ArrayList<>();
            	titles.add(getString(R.string.online));
            	titles.add(getString(R.string.offline));
            	for (int i = 0; i < titles.size(); i++) {
                	mTbGoodsList.addTab(mTbGoodsList.newTab().setText(titles.get(i)));
            	}
            	//绑定TabLayout和FrameLayout
            	manager.beginTransaction()
                    .add(R.id.fl_frame, fragments.get(0))
                    .commit();
                    //选中的样式,layout是字体样式,放在下面
                    final TextView tabSelectView = (TextView) LayoutInflater.from(当前Activity.this).inflate(R.layout.tablayout_select, null);
            	tabSelectView.setTextSize(16);//字体大小
            	tabSelectView.setText(R.string.online);//设置文字
            	//设置默认第一个选中且设置样式
           	 	mTbGoodsList.getTabAt(0).setCustomView(tabSelectView);
            	//点击tablayout的点击事件
            	mTbGoodsList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                //点击
                    switchFragment(tab.getPosition());
                    tabSelectView.setText(tab.getText());
                    tab.setCustomView(tabSelectView);
                }
    
                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                //没有被点击
                    tab.setCustomView(null);
                }
    
                @Override
                public void onTabReselected(TabLayout.Tab tab) {
                //重新被点击
                }
            });
            	//方法类,用于切换fragment
         private void switchFragment(int position) {
            FragmentTransaction transaction = manager.beginTransaction();
            Fragment showFragment = fragments.get(position);
            Fragment hideFragment = fragments.get(hide);
            if (!showFragment.isAdded()) {
                transaction.add(R.id.fl_frame, showFragment);
            }
            transaction.show(showFragment);
            transaction.hide(hideFragment);
            transaction.commit();
            hide = position;
        }
                
    
    

    对应的acvtivity的样式(仅供参考)

    	<com.google.android.material.tabs.TabLayout
            android:id="@+id/tb_goods_list"
            android:layout_width="match_parent"
            android:layout_height="44dp"
            app:tabGravity="fill"
            app:tabMaxWidth="0dp"
            app:tabMode="fixed"
            app:tabTextAppearance="@style/TabLayoutTextStyle" />
            
        <FrameLayout
            android:id="@+id/fl_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    

    我的R.layout.tablayout_select的xml样式(仅供参考)

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:textSize="18sp"
        android:layout_gravity="center"
        android:gravity="center"
        android:layout_marginHorizontal="16dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    

    如果有什么不懂的可以私信或者在下面评论嗷

    更多相关内容
  • 1.先看下实现的效果图页面失效效果图运行的动态效果图2.具体实现的方法和步骤(1)....实现自定义的卡片控件CardView,继承FrameLayout控件类,具体实现的代码如下public class CardView extends FrameLayout {pr...

    1.先看下实现的效果图

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    页面失效效果图

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    运行的动态效果图

    2.具体实现的方法和步骤

    (1).代码架构图

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    代码结构图

    (2).在这里需要引入第三方的jar包

    nineoldandroids-2.4.0.jar这个可以在网上下载到

    (3).实现自定义的卡片控件CardView,继承FrameLayout控件类,具体实现的代码如下

    public class CardView extends FrameLayout {

    private static final int ITEM_SPACE = 40;

    private static final int DEF_MAX_VISIBLE = 4;

    private int mMaxVisible = DEF_MAX_VISIBLE;

    private int itemSpace = ITEM_SPACE;

    private float mTouchSlop;

    private ListAdapter mListAdapter;

    private int mNextAdapterPosition;

    private SparseArray viewHolder = new SparseArray();

    private OnCardClickListener mListener;

    private int topPosition;

    private Rect topRect;

    public interface OnCardClickListener {

    void onCardClick(View view, int position);

    }

    public CardView(Context context, AttributeSet attrs, int defStyle) {

    super(context, attrs, defStyle);

    init();

    }

    public CardView(Context context, AttributeSet attrs) {

    super(context, attrs);

    init();

    }

    public CardView(Context context) {

    super(context);

    init();

    }

    private void init() {

    topRect = new Rect();

    ViewConfiguration con = ViewConfiguration.get(getContext());

    mTouchSlop = con.getScaledTouchSlop();

    }

    public void setMaxVisibleCount(int count) {

    mMaxVisible = count;

    }

    public int getMaxVisibleCount() {

    return mMaxVisible;

    }

    public void setItemSpace(int itemSpace) {

    this.itemSpace = itemSpace;

    }

    public int getItemSpace() {

    return itemSpace;

    }

    public ListAdapter getAdapter() {

    return mListAdapter;

    }

    public void setAdapter(ListAdapter adapter) {

    if (mListAdapter != null) {

    mListAdapter.unregisterDataSetObserver(mDataSetObserver);

    }

    mNextAdapterPosition = 0;

    mListAdapter = adapter;

    adapter.registerDataSetObserver(mDataSetObserver);

    removeAllViews();

    ensureFull();

    }

    public void setOnCardClickListener(OnCardClickListener listener) {

    mListener = listener;

    }

    private void ensureFull() {

    while (mNextAdapterPosition < mListAdapter.getCount()

    && getChildCount() < mMaxVisible) {

    int index = mNextAdapterPosition % mMaxVisible;

    View convertView = viewHolder.get(index);

    final View view = mListAdapter.getView(mNextAdapterPosition,

    convertView, this);

    view.setOnClickListener(null);

    viewHolder.put(index, view);

    // 添加剩余的View时,始终处在最后

    index = Math.min(mNextAdapterPosition, mMaxVisible - 1);

    ViewHelper.setScaleX(view,((mMaxVisible - index - 1) / (float) mMaxVisible) * 0.2f + 0.8f);

    int topMargin = (mMaxVisible - index - 1) * itemSpace;

    ViewHelper.setTranslationY(view, topMargin);

    ViewHelper.setAlpha(view, mNextAdapterPosition == 0 ? 1 : 0.5f);

    LayoutParams params = (LayoutParams) view.getLayoutParams();

    if (params == null) {

    params = new LayoutParams(LayoutParams.MATCH_PARENT,

    LayoutParams.WRAP_CONTENT);

    }

    addViewInLayout(view, 0, params);

    mNextAdapterPosition += 1;

    }

    // requestLayout();

    }

    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int childCount = getChildCount();

    int maxHeight = 0;

    int maxWidth = 0;

    for (int i = 0; i < childCount; i++) {

    View child = getChildAt(i);

    this.measureChild(child, widthMeasureSpec, heightMeasureSpec);

    int height = child.getMeasuredHeight();

    int width = child.getMeasuredWidth();

    if (height > maxHeight) {

    maxHeight = height;

    }

    if (width > maxWidth) {

    maxWidth = width;

    }

    }

    int desireWidth = widthSize;

    int desireHeight = heightSize;

    if (widthMode == MeasureSpec.AT_MOST) {

    desireWidth = maxWidth + getPaddingLeft() + getPaddingRight();

    }

    if (heightMode == MeasureSpec.AT_MOST) {

    desireHeight = maxHeight + (mMaxVisible - 1) * itemSpace + getPaddingTop() + getPaddingBottom();

    }

    setMeasuredDimension(desireWidth, desireHeight);

    }

    @Override

    protected void onLayout(boolean changed, int left, int top, int right,

    int bottom) {

    super.onLayout(changed, left, top, right, bottom);

    View topView = getChildAt(getChildCount() - 1);

    if (topView != null) {

    topView.setOnClickListener(listener);

    }

    }

    float downX, downY;

    @Override

    public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction()) {

    case MotionEvent.ACTION_MOVE:

    if (goDown()) {

    downY = -1;

    }

    break;

    }

    return super.onTouchEvent(event);

    }

    /**

    * 下移所有视图

    */

    private boolean goDown() {

    final View topView = getChildAt(getChildCount() - 1);

    if(!topView.isEnabled()){

    return false;

    }

    // topView.getHitRect(topRect); 在4.3以前有bug,用以下方法代替

    topRect = getHitRect(topRect, topView);

    // 如果按下的位置不在顶部视图上,则不移动

    if (!topRect.contains((int) downX, (int) downY)) {

    return false;

    }

    topView.setEnabled(false);

    ViewPropertyAnimator anim = ViewPropertyAnimator

    .animate(topView)

    .translationY(

    ViewHelper.getTranslationY(topView)

    + topView.getHeight()).alpha(0).scaleX(1)

    .setListener(null).setDuration(200);

    anim.setListener(new AnimatorListenerAdapter() {

    @Override

    public void onAnimationEnd(Animator animation) {

    topView.setEnabled(true);

    removeView(topView);

    ensureFull();

    final int count = getChildCount();

    for (int i = 0; i < count; i++) {

    final View view = getChildAt(i);

    float scaleX = ViewHelper.getScaleX(view)

    + ((float) 1 / mMaxVisible) * 0.2f;

    float tranlateY = ViewHelper.getTranslationY(view)

    + itemSpace;

    if (i == count - 1) {

    bringToTop(view);

    } else {

    if ((count == mMaxVisible && i != 0)

    || count < mMaxVisible) {

    ViewPropertyAnimator

    .animate(view)

    .translationY(tranlateY)

    .setInterpolator(

    new AccelerateInterpolator())

    .setListener(null).scaleX(scaleX)

    .setDuration(200);

    }

    }

    }

    }

    });

    return true;

    }

    /**

    * 将下一个视图移到前边

    *

    * @param view

    */

    private void bringToTop(final View view) {

    topPosition++;

    float scaleX = ViewHelper.getScaleX(view) + ((float) 1 / mMaxVisible)

    * 0.2f;

    float tranlateY = ViewHelper.getTranslationY(view) + itemSpace;

    ViewPropertyAnimator.animate(view).translationY(tranlateY)

    .scaleX(scaleX).setDuration(200).alpha(1)

    .setInterpolator(new AccelerateInterpolator());

    }

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

    float currentY = ev.getY();

    switch (ev.getAction()) {

    case MotionEvent.ACTION_DOWN:

    downX = ev.getX();

    downY = ev.getY();

    break;

    case MotionEvent.ACTION_MOVE:

    float distance = currentY - downY;

    if (distance > mTouchSlop) {

    return true;

    }

    break;

    }

    return false;

    }

    public static Rect getHitRect(Rect rect, View child) {

    rect.left = child.getLeft();

    rect.right = child.getRight();

    rect.top = (int) (child.getTop() + ViewHelper.getTranslationY(child));

    rect.bottom = (int) (child.getBottom() + ViewHelper

    .getTranslationY(child));

    return rect;

    }

    private final DataSetObserver mDataSetObserver = new DataSetObserver() {

    @Override

    public void onChanged() {

    super.onChanged();

    }

    @Override

    public void onInvalidated() {

    super.onInvalidated();

    }

    };

    private OnClickListener listener = new OnClickListener() {

    @Override

    public void onClick(View v) {

    if (mListener != null) {

    mListener.onCardClick(v, topPosition);

    }

    }

    };

    }

    (4).实现自定义的卡片控件之后,下面需要为这个CardView实现适配器Adapter了,在此我们定义的是一个抽象Adapter类,需要用户根据自己的实际业务场景继承这个抽象类即可,具体代码如下:

    public abstract class CardAdapter extends BaseAdapter {

    private final Context mContext;

    private ArrayList mData;

    public CardAdapter(Context context) {

    mContext = context;

    mData = new ArrayList();

    }

    public CardAdapter(Context context, Collection extends T> items) {

    mContext = context;

    mData = new ArrayList(items);

    }

    @Override

    public View getView(int position, View convertView, ViewGroup parent) {

    FrameLayout wrapper = (FrameLayout) convertView;

    View cardView;

    View convertedCardView;

    if (wrapper == null) {

    wrapper = new FrameLayout(mContext);

    wrapper.setBackgroundResource(R.drawable.card_background_shadow);

    cardView = getCardView(position, null, wrapper);

    wrapper.addView(cardView);

    } else {

    cardView = wrapper.getChildAt(0);

    convertedCardView = getCardView(position, cardView, wrapper);

    //要先删除,然后再添加,否则界面不更新

    wrapper.removeView(cardView);

    wrapper.addView(convertedCardView);

    if (convertedCardView != cardView) {

    }

    }

    return wrapper;

    }

    protected abstract View getCardView(int position, View convertView, ViewGroup parent);

    public void addAll(List items){

    mData.addAll(items);

    }

    @Override

    public T getItem(int position) {

    return mData.get(position);

    }

    @Override

    public int getCount() {

    return mData.size();

    }

    @Override

    public long getItemId(int position) {

    return getItem(position).hashCode();

    }

    public Context getContext() {

    return mContext;

    }

    public void clear(){

    if(mData != null){

    mData.clear();

    }

    }

    }

    (5).在这个卡片控件实现过程中,需要根据手机屏幕大小来自适应卡片控件的大小,所以需要一个方法来计算手机屏幕大小,我们抽象成一个工具类来实现,代码如下:

    public class Utils {

    public float convertPixelsToDp(Context ctx, float px) {

    DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();

    float dp = px / (metrics.densityDpi / 160f);

    return dp;

    }

    public static int convertDpToPixelInt(Context context, float dp) {

    DisplayMetrics metrics = context.getResources().getDisplayMetrics();

    int px = (int) (dp * (metrics.densityDpi / 160f));

    return px;

    }

    public static float convertDpToPixel(Context context, float dp) {

    DisplayMetrics metrics = context.getResources().getDisplayMetrics();

    float px = (float) (dp * (metrics.densityDpi / 160f));

    return px;

    }

    }

    (6).在Activity来实现卡片布局的显示了,很快就能看到具体的实现效果了。

    public class MainActivity extends FragmentActivity implements OnCardClickListener{

    List list;

    private TestFragment frag;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    initUI();

    }

    private void initUI() {

    CardView cardView = (CardView) findViewById(R.id.cardView1);

    cardView.setOnCardClickListener(this);

    cardView.setItemSpace(Utils.convertDpToPixelInt(this, 20));

    MyCardAdapter adapter = new MyCardAdapter(this);

    adapter.addAll(initData());

    cardView.setAdapter(adapter);

    FragmentManager manager = getSupportFragmentManager();

    frag = new TestFragment();

    manager.beginTransaction().add(R.id.contentView, frag).commit();

    }

    @Override

    public void onCardClick(final View view, final int position) {

    Toast.makeText(MainActivity.this, position + "", Toast.LENGTH_SHORT).show();

    Bundle bundle = new Bundle();

    bundle.putString("text", list.get(position%list.size()));

    frag.show(view,bundle);

    }

    private List initData() {

    list = new ArrayList();

    list.add("七仔:");

    list.add("楠楠:");

    list.add("小编:");

    list.add("陈儿:");

    list.add("小小:");

    list.add("Zz:");

    list.add("牵着蜗牛去西藏:");

    return list;

    }

    public class MyCardAdapter extends CardAdapter{

    public MyCardAdapter(Context context) {

    super(context);

    }

    @Override

    public int getCount() {

    return Integer.MAX_VALUE;

    }

    @Override

    protected View getCardView(int position,

    View convertView, ViewGroup parent) {

    if(convertView == null) {

    LayoutInflater inflater = LayoutInflater.from(MainActivity.this);

    convertView = inflater.inflate(R.layout.item_layout, parent, false);

    }

    TextView tv = (TextView) convertView.findViewById(R.id.textView1);

    String text = getItem(position%list.size());

    tv.setText(text);

    return convertView;

    }

    }

    }

    (7).在点击每个卡片的时候有个翻转的页面,我们是用Fragment来实现的,具体的Fragment代码如下:

    public class TestFragment extends Fragment{

    private TextView tv;

    private View root;

    private View view;

    @Override

    public View onCreateView(LayoutInflater inflater,

    @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    root = inflater.inflate(R.layout.frag_layout, container,false);

    initUI(root);

    return root;

    }

    private void initUI(final View root) {

    root.setClickable(true);

    root.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {}

    });

    tv = (TextView) root.findViewById(R.id.textView);

    root.findViewById(R.id.button).setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {

    root.setClickable(false);

    ViewPropertyAnimator.animate(root)

    .rotationY(-90).setDuration(200)

    .setListener(new AnimatorListenerAdapter(){

    @Override

    public void onAnimationEnd(Animator animation) {

    root.clearAnimation();

    root.setVisibility(View.INVISIBLE);

    view.setEnabled(true);

    }

    });

    }

    });

    }

    public void show(final View view,Bundle bundle){

    view.setEnabled(false);

    this.view = view;

    String text = bundle.getString("text");

    tv.setText(text);

    ViewHelper.setRotationY(view, 0);

    ViewHelper.setRotationY(root, -90);

    root.setVisibility(View.VISIBLE);

    ViewPropertyAnimator.animate(view).rotationY(90)

    .setDuration(300).setListener(null)

    .setInterpolator(new AccelerateInterpolator());

    ViewPropertyAnimator.animate(root)

    .rotationY(0).setDuration(200).setStartDelay(300)

    .setListener(new AnimatorListenerAdapter() {

    @Override

    public void onAnimationEnd(Animator animation) {

    ViewHelper.setRotationY(view, 0);

    }

    });

    }

    }

    3.下面给出具体的资源文件

    (1).layout布局文件

    item_layout.xml的具体布局代码如下:

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    >

    android:id="@+id/textView1"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:gravity="center"

    android:padding="20dp"

    android:textAppearance="?android:attr/textAppearanceMedium"

    />

    android:id="@+id/imageView1"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:layout_below="@+id/textView1"

    android:text="@string/text"

    android:textAppearance="?android:attr/textAppearanceMedium"

    android:gravity="center"

    android:layout_marginTop="30dp"

    android:layout_marginBottom="30dp"

    />

    activity_main的布局文件代码如下:

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context="com.DevStoreDemo.MainActivity"

    android:background="#00688B"

    android:clipChildren="false" >

    android:id="@+id/cardView1"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:clipChildren="false"/>

    android:id="@+id/contentView"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"/>

    frag_layout.xml布局文件代码如下:

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="@android:color/white"

    android:visibility="invisible"

    >

    android:id="@+id/textView"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_centerHorizontal="true"

    android:textAppearance="?android:attr/textAppearanceLarge"

    />

    android:id="@+id/button"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_alignParentLeft="true"

    android:layout_alignParentTop="true"

    android:class="lazyload" src="https://img-blog.csdnimg.cn/2022010622472723850.png" data-original="@drawable/close_bg"

    android:padding="10dp"

    />

    android:id="@+id/imageView1"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_centerHorizontal="true"

    android:layout_centerVertical="true"

    android:class="lazyload" src="https://img-blog.csdnimg.cn/2022010622472723850.png" data-original="@drawable/a" />

    (2)values资源文件内容

    carcontainer_attrs.xml资源文件代码如下

    color.xml资源文件代码如下

    #EBEBEB

    #D0D0CE

    (3)drawable资源文件主要存放一些图片资源

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    AAffA0nNPuCLAAAAAElFTkSuQmCC

    4.以上是卡片控件CardView的一个简单的实现,有不足的地方请大家多多指教,谢谢

    展开全文
  • 功能强大,可自定义和可扩展的ViewPager指示器框架。 作为ViewPagerIndicator的最佳替代,TabLayout和PagerSlidingTabStrip。 用法 简单的步骤,您可以集成MagicIndicator : 签出MagicIndicator ,其中包含源代码...
  • 3.在TitleActivity中实现标题栏以下内容切换 首先定义标题栏 <?xml version=1.0 encoding=utf-8?> <RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android android:id=@+id/layout_...
  • 自定义标题栏ColumnHorizontalScrollView继承HorizontalScrollView 这个安卓原生的控件,HorizontalScrollView是一种FrameLayout(框架布局),其子项被滚动查看时是整体移动的,并且子项本身可以是一个有复杂层次...
  • Android_自定义View、Fragment

    千次阅读 2017-07-24 15:04:23
    自定义View①、新建类继承View ②、构造器 ③、覆写onDraw() ④、xml调用 ⑤、自定义图形 1、矩形 2、圆(初始效果与矩形类似) 除基本配置(换色、去实心)外,默认圆会有锯齿边 去掉锯齿 画弧...

    自定义View

    ①、新建类继承View
    这里写图片描述

    ②、构造器

    这里写图片描述
    ③、覆写onDraw()
    这里写图片描述
    ④、xml调用
    这里写图片描述

    ⑤、自定义图形
    1、矩形
    这里写图片描述

    这里写图片描述
    这里写图片描述

    这里写图片描述
    2、圆(初始效果与矩形类似)
    这里写图片描述

    除基本配置(换色、去实心)外,默认圆会有锯齿边

    这里写图片描述
    去掉锯齿

    这里写代码片
    这里写图片描述
    画弧(通过矩形)

    这里写图片描述
    起始点右侧

    这里写图片描述
    第四个参数为true时

    这里写图片描述
    将圆心与两边相连

    这里写图片描述

    根据矩形画内切圆(椭圆等。。)

    这里写图片描述

    这里写图片描述

    ③、画图片

    这里写图片描述
    通过子线程实现图片位移
    这里写图片描述
    每次改变x、y后需要重新绘制图片—>postInvalidate() 子线程重绘

    通过触碰监听实现图片拖动

    列表内容

    每次修改x、y值后需要重绘图片—->invalidate() 主线程中的重绘

    ④、自定义View的动画
    动画属性(矩阵):
    这里写图片描述
    位移:
    这里写图片描述
    旋转:
    这里写图片描述
    缩放:
    这里写图片描述
    倾斜:
    这里写图片描述
    注:以上动画同时使用时只有最后一个动画生效

    多个动画实现:

    这里写图片描述

    这里写图片描述

    Fragment(Android 碎片):
    1、作用
    同一管理一部分视图的布局

    2、版本
    3.0及以后–>兼容
    2.+–>用V4包
    注意:
    1>Activity必须继承一个特殊的父类FragmentActivity
    2>andorid studio向导创建的fragment就是v4
    3>如果决定用v4的fragment,那么其他的配件就也要是v4的
    4>this.getSupportFragmentManager

    3、静态与动态使用方式
    静态:
    构建:
    ①、在res/layout/。。新建任意布局
    这里写图片描述
    fragment1:
    这里写图片描述
    fragment2:
    这里写图片描述
    ②、创建java,加载布局
    这里写图片描述
    Fragment2
    这里写图片描述

    ③、在主布局xml中调用fragment

    这里写图片描述
    在fragment标签中,通过name属性引用java文件中渲染的fragment

    ④、结果

    将两个布局加载到一个xml中
    这里写图片描述
    可以看出:fragment是activity的一部分,负责activity中一部分视图的构建。只要创建好一个视图,通过java渲染成fragment,再由主xml调用即可(团队开发时,每人负责一个界面,最后通过fragment整合,方便管理)
    静态构建:普通加载在界面上,固定在界面,不会改变(区分动态)

    动态(事务管理:切换、添加、移除、显现、隐藏):
    构建
    构建方式与静态相同–>1、新建布局 2、java构建渲染 3、主xml调用(不指明调用哪个–没有name属性)
    不同点在于—>动态能够在区域内切换、添加、隐藏、移除fragment
    (类似QQ点击下列按钮,中间视图不断切换)

    这里写图片描述
    事务管理
    add 添加 不能重复添加
    remove 移除
    replace 替换 =remove+add
    hide 隐藏
    show 让隐藏重现
    detech 销毁视图结构
    attech 重构视图结构
    ①、建立
    这里写图片描述
    ②、获取工具
    这里写图片描述
    注意:在主xml中通过FrameLayout预留出fragment的位置,不需要指定fragment布局
    这里写图片描述
    1、添加
    这里写图片描述
    2、移除
    这里写图片描述
    (隐藏、销毁都需要在已存在Fragment前提下;显示、重构无法实现add功能)
    3、显示
    这里写图片描述
    4、隐藏
    这里写图片描述
    5、重构
    这里写图片描述
    6、销毁
    这里写图片描述
    7、替换
    这里写图片描述
    add remove 与 hide和show attech和detech间的区别

      他们都能实现看得见--->看不见--->看得见 
    
      性能消耗和内存处理上不同
      remove 只得是完全销毁我们fragment的全部构造数据
    
      hide  关于内存中的数据什么都不动,只是变得看不见了而已 
    
      detech 只是把视图部分销毁,其他数据还在
    

    4、生命周期
    (可见在activity、fragment创建之前,fragment就会执行与activity界面绑定的方法)

    这里写图片描述
    Fragment的生命周期方法需要在继承Fragment的类中覆写

    Fragment生命周期分析:
    1. 当一个fragment被创建的时候,它会经历以下状态.

        onAttach()
        onCreate()
        onCreateView()
        onActivityCreated()
    1. 当这个fragment对用户可见的时候,它会经历以下状态。
        onStart()
        onResume()
    1. 当这个fragment进入“后台模式”的时候,它会经历以下状态。
        onPause()
        onStop()
    1. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。
        onPause()
        onStop()
        onDestroyView()
        onDestroy() 
        onDetach()
    1. 就像activitie一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。
        onCreate()
        onCreateView()
        onActivityCreated()
    1. fragments的大部分状态都和activitie很相似,但fragment有一些新的状态。
      onAttached() —— 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
      onCreateView() —— 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
      onActivityCreated() —— 当activity的onCreated()方法返回后调用此方法
      onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
      onDetach() —— 当fragment和activity分离的时候,调用这个方法。
      一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。

    添加与移除的生命周期
    ①、添加
    这里写图片描述
    ②、移除
    这里写图片描述
    完全创建、完全销毁

    销毁与重构的生命周期
    ①、销毁
    这里写图片描述
    ②、重构
    这里写图片描述
    并没有完全销毁、只销毁了视图结构和创建视图结构

    隐藏与显示的声明周期
    ①、显示、销毁

    声明周期没有变化

    替换声明周期
    1–>2
    这里写图片描述
    2–>1

    5、实例QQ界面

    ①、替换(添加移除、构建销毁—>耗时)

    这里写图片描述

    这里写图片描述
    ②、隐藏,显示

    这里写图片描述

    这里写图片描述
    这里写图片描述
    隐藏显示操作不改变视图数据、不涉及声明周期操作,效率较高

    6、Fragment的回退栈操作

    fragment相对于Activity是轻量级的布局控件,在应用中可以用fragment铺满activity来代替activity界面,这样多个activity界面可以集成在一个activity中,通过fragment跳转切换来实现activity界面的跳转

    默认设置显示fragment
    这里写图片描述
    在fragment1中设置监听跳转fragment2
    这里写图片描述

    能够实现fragment的跳转,但是多个fragment在同一个activity上,按下Back会直接退出程序

    修改:在fragment1中提交之前加入会退栈
    这里写图片描述
    针对不同手机可能没有效果,如果无效则需要在activity中覆写onBackPressed方法
    这里写图片描述

    7、控件实例化
    在6中可以看出,实例化fragment中的控件,需要在该fragment的java类中进行实例化
    这里写图片描述

    8、在fragment中获取上下文
    在fragment中没有继承或实现Context的类或接口,所以当前fragment对象无法用作上下文
    但是可以通过当前对象获取到上下文
    注:切忌构造器传参

    这里写图片描述

    9、V4包下的Fragment
    注意点:
    1>Activity必须继承一个特殊的父类FragmentActivity

       2>studio向导创建的fragment就是v4
    
       3>如果决定用v4的fragment,那么其他的配件就也要是v4的
    
       4>this.getSupportFragmentManager(获取fragment管理器的方法)
    

    10、传值

    一、activity—>fragment
    ( setArguments(Bundle) && getArguments() )
    在 MainActivity中:

    这里写图片描述
    在Fragment中:

    这里写图片描述
    二、fragment—>activity
    ( 回调函数 )

    在Fragment中
    声明回调接口—>提供set方法—>调用接口中的方法
    1、
    这里写图片描述
    2、
    这里写图片描述
    3、
    这里写图片描述

    在MainActivity中实现接口

    这里写图片描述

    展开全文
  • 概念Android的界面是有布局和组件协同完成的,...Android的五大布局分别是LinearLayout(线性布局)、FrameLayout(单帧布局)、RelativeLayout(相对布局)、AbsoluteLayout(绝对布局)和TableLayout(表格布局)。

    概念

    Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦。组件按照布局的要求依次排列,就组成了用户所看见的界面。Android的五大布局分别是LinearLayout(线性布局)、FrameLayout(单帧布局)、RelativeLayout(相对布局)、AbsoluteLayout(绝对布局)和TableLayout(表格布局)。

    LinearLayout

    LinearLayout按照垂直或者水平的顺序依次排列子元素,可以通过属性orientation设定垂直vertical还是水平horizontal,每一个子元素都位于前一个元素之后。如果是垂直排列,那么将是一个N行单列的结构,每一行只会有一个元素,而不论这个元素的宽度为多少;如果是水平排列,那么将是一个单行N列的结构。如果搭建两行两列的结构,通常的方式是先垂直排列两个元素,每一个元素里再包含一个LinearLayout进行水平排列。
    这里写图片描述 这里写图片描述
    LinearLayout有个重要属性layout_weight(权重),它用于描述该子元素在剩余空间中占有的大小比例。例如我们可以对于上图水平排序的三个按钮分别添加
    android:layout_weight=“1”,它表示,三个按钮的各占用父元素(也就是这一整行)一份权重,也就是各三分之一。当只有一个按钮添加了权重时,它表示第二第三个按钮不参与权重分隔,它们的大小是包裹内容的大小,父元素剩下的部分被第一个按钮全部占用(因为只有它)。
    这里写图片描述 这里写图片描述

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个按钮"
        android:layout_weight="1"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn2"
        android:text="第二个按钮"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn3"
        android:text="第三个按钮"
        />

    FrameLayout

    FrameLayout是五大布局中最简单的一个布局,在这个布局中,整个界面被当成一个九宫格。如果控件放在同一个九宫格里面,那么后一个就会将前一个覆盖。
    FrameLayout布局控件的位置通过属性android:layout_gravity=”“确定,显然它有九个值:left|top、top、right|top、left|center、center、right|center、left|bottom、bottom、right|bottom。
    这里写图片描述 这里写图片描述

    <Button
        android:layout_width="180dp"
        android:layout_height="140dp"
        android:text="第一个按钮"
        android:background="#ff0000"
        android:id="@+id/button1"
        android:layout_gravity="left|top" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个按钮"
        android:background="#00ff00"
        android:id="@+id/button2"
        android:layout_gravity="left|top" />

    RelativeLayout

    顾名思义,相对布局RelativeLayout按照各子元素之间的相对位置关系完成布局。在此布局中的子元素里与位置相关的属性将生效。例如android:layout_below, android:layout_above等。子元素就通过这些属性和各自的ID配合指定位置关系。注意在指定位置关系时,引用的ID必须在引用之前,先被定义,否则将出现异常。
    RelativeLayout里常用的位置属性如下:
    android:layout_toLeftOf —— 该组件位于引用组件的左方
    android:layout_toRightOf —— 该组件位于引用组件的右方
    android:layout_above —— 该组件位于引用组件的上方
    android:layout_below —— 该组件位于引用组件的下方
    android:layout_alignParentLeft —— 该组件是否对齐父组件的左端
    android:layout_alignParentRight —— 该组件是否对齐其父组件的右端
    android:layout_alignParentTop —— 该组件是否对齐父组件的顶部
    android:layout_alignParentBottom —— 该组件是否对齐父组件的底部
    android:layout_centerInParent —— 该组件是否相对于父组件居中
    android:layout_centerHorizontal —— 该组件是否横向居中
    android:layout_centerVertical —— 该组件是否垂直居中
    这里写图片描述

    <Button
       android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个按钮"
        android:id="@+id/button2"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个按钮"
        android:id="@+id/button3"
        android:layout_below="@+id/button2"
        android:layout_toRightOf="@+id/button2"
        android:layout_toEndOf="@+id/button2" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三个按钮"
        android:id="@+id/button5"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />

    AbsoluteLayout

    AbsoluteLayout布局时,整个平面任何位置都可以随意放置控件,在此布局中的子元素的android:layout_x和android:layout_y属性将生效,用于描述该子元素的坐标位置。屏幕左上角为坐标原点(0,0),第一个0代表横坐标,向右移动此值增大,第二个0代表纵坐标,向下移动,此值增大。在此布局中的子元素可以相互重叠。在实际开发中,通常不采用此布局格式,因为它的界面代码过于刚性,以至于有可能不能很好的适配各种终端。
    这里写图片描述

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个按钮"
        android:id="@+id/button2"
        android:layout_x="59dp"
        android:layout_y="63dp" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个按钮"
        android:id="@+id/button3"
        android:layout_x="157dp"
        android:layout_y="303dp" />

    TableLayout

    顾名思义,TableLayout为表格布局,适用于N行N列的布局格式。一个TableLayout由许多TableRow组成,一个TableRow就代表TableLayout中的一行。
      TableRow是LinearLayout的子类,它的android:orientation属性值恒为horizontal,并且它的android:layout_width和android:layout_height属性值恒为MATCH_PARENT和WRAP_CONTENT。所以它的子元素都是横向排列,并且宽高一致的。这样的设计使得每个TableRow里的子元素都相当于表格中的单元格一样。在TableRow中,单元格可以为空,但是不能跨列。
    这里写图片描述

     <TableRow
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:text="第一个按钮"
            android:id="@+id/button2"
            android:layout_column="0" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="第二个按钮"
            android:id="@+id/button3"
            android:layout_column="1" />
    </TableRow>
    
    <TableRow
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:text="第三个按钮"
            android:id="@+id/button4"
            android:layout_column="1" />
    </TableRow>

    自定义布局

    在实际开发当中,我们肯定见到过各式各样复杂的布局,如植物大战僵尸、愤怒的小鸟等。显然,这种精致的布局是不可能通过上述五大布局来实现的,这就需要我们来自定义布局。
    我们主要是通过以下五个方面创建一个自定义View
    1,绘图,通过重写onDraw方法控制View在屏幕上的渲染效果
    2,交互,通过重写onTouchEvent方法或者使用手势来控制用户的交互
    3,测量,通过重写onMeasure方法来对控件进行测量
    4,属性,可以通过xml自定义控件的属性,然后通过TypedArray来进行使用
    5,状态的保存,为了避免配置改变时丢失View状态,通过重写onSaveInstanceState,onRestoreInstanceState方法来保存和恢复状态

    总结起来,自定义布局有两大优点:

    1.通过减少view的使用和更快地遍历布局元素让你的UI显示更加有效率;
    2.可以构建那些无法由已有的view实现的UI。

    自定义布局的流程

    下面我们以一个简单的绘图实例来演示自定义布局的流程

    一、自定义View类继承View

    为了创建点击可切换的形状的自定义View,我们继承View,编写构造方法。实现三个构造方法,最终调用三个参数的构造方法。

    public class CustomView extends View {
    
       public CustomView(Context context) {
           this(context, null);
       }
    
       public CustomView(Context context, AttributeSet attrs) {
           this(context, attrs, 0);
       }
    
       public CustomView(Context context, AttributeSet attrs, int defStyle) {
           super(context, attrs, defStyle);
       }
    }
    二、将自定义view加入到layout中

    注意,自定义view的名称必须包含全路径,即所有的包名。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.example.administrator.myapplication.CustomView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/customview"
            android:layout_centerInParent="true"
            />
    
    </RelativeLayout>
    三、给自定义View添加自定义属性

    一个良好的自定义控件应该是能通过xml进行控制的,所以我们需要考虑一下我们的自定义View的哪些属性需要被提取到xml中,比如,我们应该可以让用户选择图形的颜色,是否显示图形的名称等。我们可以通过下面的代码在xml中进行配置。

    <com.example.administrator.myapplication.CustomView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/customview"
            android:layout_centerInParent="true"
            xmlns:app="http://schemas.android.com/apk/res/
                      com.example.administrator.myapplication"
            app:displayShapeName="true"
            app:shapeColor="#7f0000"
            />

    为了能够使用图形的颜色和图形显示的名字的属性,我们应该新建res/values/attrs.xml文件,在里面定义这些属性。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
       <declare-styleable name="CustomView">
           <attr name="shapeColor" format="color" />
           <attr name="displayShapeName" format="boolean" />
       </declare-styleable>
    </resources>

    注意上述代码,我们为每一个attr节点都写了name属性和format属性,format是属性的数据结构,合法的值包括string, color, dimension, boolean, integer, float, enum等

    四、应用自定义属性

    现在我们已经通过xml设定了自定义属性shapeColor和displayShapeName,我们需要在构造方法中提取到这些属性。为了提取属性,我们使用TypedArray类和obtainStyledAttributes方法。

    public class CustomView extends View {
        private int shapeColor;
        private boolean displayShapeName;
    
        public CustomView(Context context) {
            this(context, null);
        }
    
        public CustomView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CustomView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            setupAttributes(attrs);
        }
    
        private void setupAttributes(AttributeSet attrs) {
            // 提取自定义属性到TypedArray对象中
            TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs,
                    R.styleable.CustomView, 0, 0);
            // 将属性赋值给成员变量
            try {
                shapeColor = a.getColor(R.styleable.CustomView_shapeColor,
                        Color.BLACK);
                displayShapeName = a.getBoolean(
                        R.styleable.CustomView_displayShapeName, false);
            } finally {
                // TypedArray对象是共享的必须被重复利用。
                a.recycle();
            }
        }
    }
    五、增加属性的getter和setter方法
    public boolean isDisplayingShapeName() {
        return displayShapeName;
      }
    
      public void setDisplayingShapeName(boolean state) {
        this.displayShapeName = state;
        invalidate();//重绘
        requestLayout();
      }
    
      public int getShapeColor() {
        return shapeColor;
      }
    
      public void setShapeColor(int color) {
        this.shapeColor = color;
        invalidate();
        requestLayout();
      }

    注意以上代码,当View的属性发生改变时我们需要进行重绘和重新布局,为了保证正常进行,请确保调用了invalidate和requestLayout方法。

    六、绘制图形

    接下来,让我们开始真正使用自定义属性(颜色,是否显示图形名)进行图形的绘制。所有的View的绘制发生在onDraw方法里,我们使用其参数Canvas将图形绘制到View上,现在我们绘制一个正方形。

    public class CustomView extends View {
    
        private int shapeWidth = 100;
        private int shapeHeight = 100;
        private int textXOffset = 0;
        private int textYOffset = 30;
        private Paint paintShape;
    
    
        private int currentShapeIndex = 0;
    
        public CustomView(Context context) {
            this(context, null);
        }
    
        public CustomView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CustomView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            setupAttributes(attrs);
            setupPaint();
        }
        private void setupPaint() {
            paintShape = new Paint();
            paintShape.setStyle(Style.FILL);
            paintShape.setColor(shapeColor);
            paintShape.setTextSize(30);
        }
    }

    以上代码会绘制我们定义的颜色的图形,如果显示图形名,其图形名也会被显示,效果图就跟上面的gif图片里的正方形一样。

    七、计算尺寸

    为了按照用户定义的宽度高度进行绘制,我们需要重写onMeasure方法进行View的测量,该方法决定了View的宽度和高度。我们定义的View的宽度和高度由我们的形状和形状名字共同决定。我们先看下onMeasure的代码。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // 简单定义文本边距
            int textPadding = 10;
            int contentWidth = shapeWidth;
            // 使用测量模式获得宽度
            int minw = contentWidth + getPaddingLeft() + getPaddingRight();
            int w = resolveSizeAndState(minw, widthMeasureSpec, 0);
            // 同宽度
            int minh = shapeHeight + getPaddingBottom() + getPaddingTop();
            //如果现实图形名,则加上文字高度
            if (displayShapeName) {
                minh += textYOffset + textPadding;
            }
            int h = resolveSizeAndState(minh, heightMeasureSpec, 0);
            // 测量完成后必须调用setMeasuredDimension方法
            // 之后可以通过getMeasuredWidth 和 getMeasuredHeight 方法取出高度和宽度
            setMeasuredDimension(w, h);
        }

    注意以上计算要将View的内边距计算进去然后再计算整个宽度高度,并且最后必须调用setMeasuredDimension方法设置宽度和高度,resolveSizeAndState() 方法将返回一个合适的尺寸,只要将测量模式和我们计算的宽度高度传进去即可。该方法在API11开始出现,低于该版本将无法使用该方法,这里我抽取android的源码供参考。

    /**
        * Utility to reconcile a desired size and state, with constraints imposed
        * by a MeasureSpec.  Will take the desired size, unless a different size
        * is imposed by the constraints.  The returned value is a compound integer,
        * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
        * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
        * size is smaller than the size the view wants to be.
        *
        * @param size How big the view wants to be
        * @param measureSpec Constraints imposed by the parent
        * @return Size information bit mask as defined by
        * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
        */
       public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
           int result = size;
           int specMode = MeasureSpec.getMode(measureSpec);
           int specSize =  MeasureSpec.getSize(measureSpec);
           switch (specMode) {
           case MeasureSpec.UNSPECIFIED:
               result = size;
               break;
           case MeasureSpec.AT_MOST:
               if (specSize < size) {
                   result = specSize | MEASURED_STATE_TOO_SMALL;
               } else {
                   result = size;
               }
               break;
           case MeasureSpec.EXACTLY:
               result = specSize;
               break;
           }
           return result | (childMeasuredState&MEASURED_STATE_MASK);
       }

    该方法里设计到了两处位运算,暂时还没搞懂这两处位运算有什么作用,如果有清除的还请帮忙解释下作用。

    八、在不同图形之间进行切换

    现在我们已经绘制了正方形,但是我们想让view在我们点击它的时候切换图形,现在我们给它加入事件,我们重写onTouchEvent方法即可

    private String[] shapeValues = { "square", "circle", "triangle" };
      private int currentShapeIndex = 0;
      @Override
      public boolean onTouchEvent(MotionEvent event) {
        boolean result = super.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
          currentShapeIndex ++;
          if (currentShapeIndex > (shapeValues.length - 1)) {
            currentShapeIndex = 0;
          }
          postInvalidate();
          return true;
        }
        return result;
      }

    现在无论什么时候点击view,都会选中对应的形状,当postInvalidate 方法被调用后就会进行重绘,现在我们更新onDraw代码,绘制不同的图形。

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        String shapeSelected = shapeValues[currentShapeIndex];
        if (shapeSelected.equals("square")) {
          canvas.drawRect(0, 0, shapeWidth, shapeHeight, paintShape);
          textXOffset = 0;
        } else if (shapeSelected.equals("circle")) {
          canvas.drawCircle(shapeWidth / 2, shapeHeight / 2, shapeWidth / 2, paintShape);
          textXOffset = 12;
        } else if (shapeSelected.equals("triangle")) {
          canvas.drawPath(getTrianglePath(), paintShape);
          textXOffset = 0;
        }
        if (displayShapeName) {
          canvas.drawText(shapeSelected, 0 + textXOffset, shapeHeight + textYOffset, paintShape);
        }
      }
    
      protected Path getTrianglePath() {
        Point p1 = new Point(0, shapeHeight), p2 = null, p3 = null;
        p2 = new Point(p1.x + shapeWidth, p1.y);
        p3 = new Point(p1.x + (shapeWidth / 2), p1.y - shapeHeight);
        Path path = new Path();
        path.moveTo(p1.x, p1.y);
        path.lineTo(p2.x, p2.y);
        path.lineTo(p3.x, p3.y);
        return path;
      }

    现在我们点击view,每点击一次图形就会进行切换,其效果图就跟最初贴的gif图片一样。

    九、完善控件

    增加getter方法获得图形名

    public String getSelectedShape() {
        return shapeValues[currentShapeIndex];
    }

    现在在activity中,我们就可以通过getSelectedShape可以获取到图形名了。

    十、状态的保存

    当配置改变时,比如手机屏幕发生旋转,我们必须保存一些数据供从容保证view的状态不会发生改变。我们通过重写onSaveInstanceState和onRestoreInstanceState方法来保存和恢复数据。比如,在我们的view中,我们需啊哟保存的数据是当前是什么图形,可以通过保存数组的下标currentShapeIndex来实现。

    @Override
      public Parcelable onSaveInstanceState() {
        // 新建一个Bundle
        Bundle bundle = new Bundle();
        // 保存view基本的状态,调用父类方法即可
        bundle.putParcelable("instanceState", super.onSaveInstanceState());
        // 保存我们自己的数据
        bundle.putInt("currentShapeIndex", this.currentShapeIndex);
        // 当然还可以继续保存其他数据
        // 返回bundle对象
        return bundle;
      }
    
      @Override
      public void onRestoreInstanceState(Parcelable state) {
        // 判断该对象是否是我们保存的
        if (state instanceof Bundle) {
          Bundle bundle = (Bundle) state;
          // 把我们自己的数据恢复
          this.currentShapeIndex = bundle.getInt("currentShapeIndex");
          // 可以继续恢复之前的其他数据
          // 恢复view的基本状态
          state = bundle.getParcelable("instanceState");
        }
        // 如果不是我们保存的对象,则直接调用父类的方法进行恢复
        super.onRestoreInstanceState(state);
      }

    一旦我们定义这些保存和恢复的方法,我们就能够在配置发生改变时保存我们必要的数据。
    这里写图片描述 这里写图片描述 这里写图片描述

    展开全文
  • Android自定义相机,切换前后摄像头,照相机拍照Ctrl +C Ctrl+V 可以直接 run 起来,注释比较详细;源码下载 package="com.tomorrow_p.camera_p">
  • 广告轮播图在现在的APP首页比较常见,主要的实现方式有两种,一种是通过ViewPager,一种是通过自定义ViewGroup。前者的实现方式比较简便,本篇文章讲的是第二种方法,有人说用ViewPager不是更方便吗,的确,但是我们...
  • 自定义BreadCrumbView,继承FrameLayout2.使用自定义BreadCrumbView3.使用效果3.项目目录:二、树型结构布局(TreeListView)1.引入库三、分页布局(jetpack的paging库)1.自定义控件2.使用自定义控件总结 前言 ...
  • 关于android自定义底部菜单实现与Fragment间的切换,实现过程大体如下:attrs.xml中的内容:&lt;resources&gt; &lt;attr name="text" format="string"/&gt; &lt;attr ...
  • 自定义的tab切换

    千次阅读 2016-09-14 15:04:58
    这里的例子比较简单,其实就是用布局写一个tab切换,不用系统的控件或是第三方控件,样式可以自己定义,只要喜欢什么都可以添加,效果图如下: (这里设计最后一个要求有小图标,其他的不需要,如果都需要可以把判断...
  • 这样每一个ViewPager就有Padding部分来预览左右的内容,但是这样还不够,我们还需要设置FrameLayout 的 android:clipChildren=“false”,这个属性是代表FrameLayout 允许子控件的内容超出其内容,不对超出的部分...
  • 1. 列举常用的状态:`加载中`、`网络错误`、`错误`、`空数据`、`正常内容`、`其他...` 2. 不同状态的view可以... 可以在状态切换之前/之后监听,并且拦截是否真正的进行状态切换 5. 其他优化,是否包含切换动画等等
  • 【2】实现FragmentTabHost切换Fragment关联切换 tab_indicator  android:orientation="vertical" android:layout_width="match_parent"  android:gravity="center"  android:paddingTop="10dp...
  • 自定义ViewGroup可按父类分为三类,分别为继承自ViewGroup、继承自系统特定的ViewGroup(如LinearLayout)和继承自View。 其中第二种最为简单,第三种最为复杂,让我们先把目光放在第一种难度适中的情况。 目标 仿照...
  • android中的布局类,如LinearLayout,FrameLayout等,默认是不执行onDraw方法的 如果想要强制执行,则需要在构造方法里调用以下代码 //允许绘制 setWillNotDraw(false);
  • 可以看到,代码很简单,只需要调用getApplication()方法就能拿到我们自定义的Application的实例了,打印结果如下所示: 那么除了getApplication()方法,其实还有一个getApplicationContext()方法,这两个方法看上去...
  • FrameLayout(帧布局的两种实现效果) 本节引言 FrameLayout(帧布局)可以说是六大布局中最为简单的一个布局,这个布局直接在屏幕上开辟出一块空白的区域,当我们往里面添加控件的时候,会默认把他们放到这块区域的左上角,...
  • 切换时,被选中的颜色会变化 左右滑动屏幕也会切换不同的页面。 3. 代码目录结构 二、代码 1. xml tab2.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro
  • Android FrameLayout ( 帧布局 )

    千次阅读 2021-06-02 21:41:36
    Android FrameLayout (帧布局) 直接在屏幕上开辟出一块空白的区域,当我们往里面添加控件的时候,会默认把它们放到这块区域的左上角FrameLayoutFrameLayout (帧布局) 有点类似于把零散的大小不一的纸按左上角对齐的...
  • UniversalVideoView是一个Android小部件,可帮助您更轻松地播放视频,这与Android系统的本机VideoView相似,但具有更多的Media Controller自定义功能。 用法 有关此项目的有效实施,请参见示例应用程序。 下载库源...
  • android:src="@drawable/sample" android:id="@+id/pressThisButton" android:layout_y="40px" android:layout_x="20px"> 在FrameLayout里,我有一个包含按钮pressThisButton一个视图V。 当按钮被按下时,我能够改变...
  • 什么是banner组件?在许多Android应用上,比如客户端、百度美拍、应用宝等上面,都有一个可以手动滑动的小广告条,这就是banner,实际...整个组件是一个FrameLayout,里面有两个view,第一个是LinearLayout,承载了4
  • 在android客户端中我们经常有自己的定义视图,特别是用fragment的时候,用的还是...只能重新传递context过来,所以整理了下 目前遇到这样的问题大概用到了两种方法首先是自定义视图:public class BasicLineView ...
  • 自定义的输入框一般都有输入法与表情面板切换的功能,要做到美好的切换体验也是不容易的。切换时不要闪,闪的原因是输入法出现时,会把activity往上挤压,输入法隐藏时,activity又会复原,这个过程,我们的界面就会...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,801
精华内容 3,120
关键字:

自定义framelayout切换