android 选项卡界面

一  用RadioGroup和ViewPager实现选项卡界面


1 定义一个list用来存放选项卡各个界面的view 

      /**Tab页面列表*/
    private List<View> list_view = new ArrayList<View>();

    //往列表中添加界面

     list_view.add( view1 );

2  定义监听器和适配器

    radioGroup按钮监听器,如:class MyOnCheckedChangeListener implements OnCheckedChangeListener

    ViewPager内容适配器,如class MyViewPagerAdapter extends PagerAdapter

    ViewPager滑动监听器,如class MyOnPageChangeListener implements OnPageChangeListener

3 设置监听器和适配器

  viewPager.setAdapter(new MyViewPagerAdapter(list_view));//设置内容适配器
  viewPager.setOnPageChangeListener(new MyOnPageChangeListener());//设置滑动监听器
  radioGroup.setOnCheckedChangeListener(new MyOnCheckedChangeListener());//导航按钮添加状态改变监听器

MainActivity.java

public class MainActivity extends Activity {
	private ViewPager viewPager;
	/**图片下标*/
	private ImageView imageView;
	/**Tab页面列表*/
	private List<View> list_view;
	/** 图片下标的偏移量*/
	private int int_img_offset = 0;
	/**当前页卡编号*/
	private int int_current_index = 0;
	/**图片下标的宽度*/
	private int int_img_width;
	/**屏幕的宽度*/
	private int width;
	private RadioGroup radioGroup;
	public LocalActivityManager localActivityManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //用动画移动导航条下标,要获取屏幕宽度和导航条下标宽度
        DisplayMetrics dmetrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dmetrics);
		width = dmetrics.widthPixels;//获取屏幕宽度
		imageView = (ImageView) findViewById(R.id.bean_img_index);
		int_img_width = imageView.getDrawable().getIntrinsicWidth();// 获取导航条下标宽度
	
		int_img_offset = ( width / 3 - int_img_width) / 2;// 计算偏移量
		Matrix matrix = new Matrix();
		matrix.postTranslate(int_img_offset, 0);
		imageView.setImageMatrix(matrix);// 设置动画初始位置
			
		//创建一个list里面存放viewPager中显示的内容	
		list_view = new ArrayList<View>();
		View view1  = LayoutInflater.from(this).inflate(R.layout.sub_view,null);
		View view2  = LayoutInflater.from(this).inflate(R.layout.sub_view,null);
		
		localActivityManager = new LocalActivityManager(this, true);
		localActivityManager.dispatchCreate(savedInstanceState);
		//将Activity转化成View
		Intent intent2 = new Intent(this, SubActivity.class);
		View view3 = localActivityManager.startActivity("date", intent2).getDecorView();
		//往列表中添加界面
		list_view.add( view1 );
		list_view.add( view2 );
		list_view.add( view3 );
		
		viewPager = (ViewPager) findViewById(R.id.bean_view_pager);	
		viewPager.setAdapter(new MyViewPagerAdapter(list_view));//设置内容适配器
		viewPager.setOnPageChangeListener(new MyOnPageChangeListener());//设置滑动监听器
		//导航按钮
		radioGroup = (RadioGroup) findViewById(R.id.bean_radio_group);
		radioGroup.setOnCheckedChangeListener(new MyOnCheckedChangeListener());//导航按钮添加状态改变监听器
		((RadioButton) radioGroup.getChildAt(0)).performClick();//默认显示第一页
    }
    
    /**radioGroup按钮监听器,单选按钮RadioGroup、复选框CheckBox都有OnCheckedChangeListener事件*/
	private class MyOnCheckedChangeListener implements OnCheckedChangeListener {
		int int_one_width = int_img_offset * 2 + int_img_width;// 页卡1 -> 页卡2 偏移量
		@Override
		public void onCheckedChanged(RadioGroup group, int checkedId) {
			// TODO Auto-generated method stub
			/**触发点击事件按钮的序号*/
			int int_indexId = 0;
			switch (checkedId) {
			case R.id.bean_by_time:
				int_indexId = 0;
				break;
			case R.id.bean_unuse:
				int_indexId = 1;
				break;
			case R.id.bean_used:
				int_indexId = 2;
				break;
			default:
				break;
			}
			//修改viewPager中的显示页,如果修改后还是同一页不会触发OnPageChangeListener监听器的onPageSelected事件
			viewPager.setCurrentItem(int_indexId);
			
			/**android中提供了4中动画:
			 * AlphaAnimation 透明度动画效果 
			 * ScaleAnimation 缩放动画效果 
			 * TranslateAnimation 位移动画效果 
			 * RotateAnimation 旋转动画效果 
			 * 通过TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) 来定义动画 */
			//位移动画,水平(X轴)移动下标
			Animation animation = new TranslateAnimation(
					int_one_width* int_current_index, int_one_width * int_indexId,
					0, 0);
			int_current_index = int_indexId;
			animation.setFillAfter(true);// True:图片停在动画结束位置
			animation.setDuration(300);//动画持续时间
			//用动画移动导航条下面的红色细条下标
			imageView.startAnimation(animation);
		}

	}

	/** ViewPager内容适配器**/
	private class MyViewPagerAdapter extends PagerAdapter {
		private List<View> list_view;
		public MyViewPagerAdapter(List<View> list_view) {
			this.list_view = list_view;
		}
		/**这个方法,是从ViewGroup中移出当前View*/
		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView(list_view.get(position));
		}
		/**这个方法,return一个对象,这个对象表明了PagerAdapter适配器选择哪个对象放在当前的ViewPager中*/
		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			container.addView(list_view.get(position), 0);
			return list_view.get(position);
		}
		/**这个方法,是获取当前窗体界面数*/
		@Override
		public int getCount() {
			return list_view.size();
		}
		/**这个方法用于判断是否由对象生成界面*/
		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}
	}
	/**ViewPager滑动监听器**/
	private class MyOnPageChangeListener implements OnPageChangeListener {
		/**此方法是在状态改变的时候调用,其中arg0这个参数有三种状态(0,1,2)。
		 * arg0 ==1的时辰默示正在滑动,arg0==2的时辰默示滑动完毕了,arg0==0的时辰默示什么都没做。
		 * 三种状态的变化顺序为(1,2,0)*/
		public void onPageScrollStateChanged(int arg0) {
		}
		/**当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。
		 * 其中三个参数的含义分别为:
			arg0 :手指向左滑是你点击的页面位置编号,手指向右滑是将要滑到的页面
			arg1:当前页面偏移的百分比
			arg2:当前页面偏移的像素位置  */ 
		public void onPageScrolled(int arg0, float arg1, int arg2) {
//			Log.e("pager", ""+ arg0+"  "+ arg1+"  "+ arg2);
		}
		/**此方法是页面跳转完后得到调用,position是你当前选中的页面的位置编号,编号从0开始*/
		public void onPageSelected(int position) {
			//使用代码主动去调用控件的点击事件
			((RadioButton) radioGroup.getChildAt(position)).performClick();
		}
	}
}


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#ebebeb"
        android:orientation="vertical" >

        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >

            <RadioGroup
                android:id="@+id/bean_radio_group"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal" >

                <RadioButton
                    android:id="@+id/bean_by_time"
                    style="@style/BeanNavBtn"
                    android:text="时间" />

                <RadioButton
                    android:id="@+id/bean_unuse"
                    style="@style/BeanNavBtn"
                    android:text="日期" />

                <RadioButton
                    android:id="@+id/bean_used"
                    style="@style/BeanNavBtn"
                    android:text="数据" />
            </RadioGroup>
        </RelativeLayout>

        <ImageView
            android:id="@+id/bean_img_index"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:scaleType="matrix"
            android:src="@drawable/bean_industry_tab_img_index" />
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/bean_view_pager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </android.support.v4.view.ViewPager>

</LinearLayout>
demo地址: http://download.csdn.net/download/duduhali/9413166

二 用PagerTabStrip和ViewPager实现选项卡界面

    

PagerTabStrip与PagerTitleStrip用法和功能相似实现的标题栏效果很不好,不能指定一个页面一次显示一个,或者全部显示,而且标题还滑动。所以注定主流的App都没有用这个玩意的。

    这里给一个demo地址 http://download.csdn.net/download/duduhali/9413204

三 用TabHost实现带红点的选项卡界面


1 定义一个继承自TabActivity 的Activity

2 设置布局文件

  布局文件中 TabHost为一下元素的父节点,如
<!-- 必须用系统预定义的ID -->
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="fill_parent"
                android:layout_height="0.0dip"
                android:layout_weight="1.0" />
            
<!-- 必须用系统预定义的ID -->
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_weight="0.0"
                android:visibility="gone" />

3 获取tabhost实例

     布局文件中使用了系统预定义ID才可这样获取,如:tabHost = this.getTabHost();
     添加tab页,如:tabHost.addTab(tabHost.newTabSpec("1").setIndicator("1").setContent(new Intent(this, SubActivity.class)));

4 设置选项按钮         

    获取选项按钮,如:tab_button1 = (RadioButton) findViewById(R.id.mainTabs_radio1);
    给选项按钮添加监听器,如:tab_button1.setOnClickListener(this);
    在点击事件监听器的onClick方法中设置选项按钮的状态和显示的页面
按钮的状态:tab_button1.setChecked(true);
显示的tab页: tabHost.setCurrentTabByTag("1");

MainActivity.java
public class MainActivity extends TabActivity implements OnClickListener{
	private TabHost tabHost;
	private RadioButton tab_button1,tab_button2,tab_button3,tab_button4;
	private View red_dot;//红点
	/**标识当前被选中的按钮*/
	private int mState = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      //要实现这种效果只需要将TabActivity的默认布局覆盖
        setContentView(R.layout.main);
        //获取tabhost实例,布局文件中使用了系统预定义ID才可这样获取
        tabHost = this.getTabHost();
/**   重要方法
      addTab(TabHost.TabSpec tabSpec):添加一项Tab页
      clearAllTabs():清除所有与之相关联的Tab页
      getCurrentTab():返回当前Tab页
      getTabContentView():返回包含内容的FrameLayout
      newTabSpec(String tag):返回一个与之关联的新的TabSpec
*/
        //newTabSpec(String tag)设置tag标记,可用tabHost.setCurrentTabByTag(tag)设置显示页,
        //setIndicator(String title)设置导航按钮上的文字,TabWidget隐藏时不起作用
		tabHost.addTab(tabHost.newTabSpec("1").setIndicator("1")
				.setContent(new Intent(this, SubActivity.class)));
		tabHost.addTab(tabHost.newTabSpec("2").setIndicator("2")
				.setContent(new Intent(this, SubActivity2.class)));
		tabHost.addTab(tabHost.newTabSpec("3").setIndicator("3")
				.setContent(new Intent(this, SubActivity2.class)));
		tabHost.addTab(tabHost.newTabSpec("4").setIndicator("4")
				.setContent(new Intent(this, SubActivity2.class)));
/**		可动态创建一个view来做为tab页的内容
		tabHost.addTab(tabHost.newTabSpec("tab1")  
                .setIndicator("tab1", getResources().getDrawable(R.drawable.star_big_on))  
                .setContent(this));
*/
		
/**		也可从一个layout id创建各个tab页,这样做不能使用setContentView(R.layout.fragment_main)重新加载布局
		TabHost tabHost = getTabHost();  
        LayoutInflater.from(this).inflate(R.layout.tabs1, tabHost.getTabContentView(), true);//布局文件在此加载 
        tabHost.addTab(tabHost.newTabSpec("tab1")  
                .setIndicator("tab1")  
                .setContent(R.id.view1));
*/
		tab_button1 = (RadioButton) findViewById(R.id.mainTabs_radio1);
		tab_button2 = (RadioButton) findViewById(R.id.mainTabs_radio2);
		tab_button3 = (RadioButton) findViewById(R.id.mainTabs_radio3);
		tab_button4 = (RadioButton) findViewById(R.id.mainTabs_radio4);
		red_dot = (View)findViewById(R.id.mainTabs_red_dot1);
		tab_button1.setOnClickListener(this);
		tab_button2.setOnClickListener(this);
		tab_button3.setOnClickListener(this);
		tab_button4.setOnClickListener(this);
		switchState(1);
        
    }
    
    /**处理tab点击事件 切换状态*/
	private void switchState(int state) {
//		if (mState == state) {
//			return;
//		} // else continue
		mState = state;
		tab_button1.setChecked(false);
		tab_button2.setChecked(false);
		tab_button3.setChecked(false);
		tab_button4.setChecked(false);
		switch (mState) {
		case 1:
			tab_button1.setChecked(true);
			tabHost.setCurrentTabByTag("1");
			break;
		case 2:
			tab_button2.setChecked(true);
			tabHost.setCurrentTabByTag("2");
			//((SubActivity2)tabHost.getTabWidget().getChildAt(2)).setText("");
			break;
		case 3:
			tab_button3.setChecked(true);
			tabHost.setCurrentTabByTag("3");
			break;
		case 4:
			tab_button4.setChecked(true);
			tabHost.setCurrentTabByTag("4");
			break;
		default:
			break;
		}
	}
	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		switch (arg0.getId()) {
		case R.id.mainTabs_radio1:
			switchState(1);
			break;
		case R.id.mainTabs_radio2:
			switchState(2);
			break;
		case R.id.mainTabs_radio3:
			switchState(3);
			break;
		case R.id.mainTabs_radio4:
			switchState(4);
			break;
		default:
			break;
		}
	}
	/**设置tab上的红点是否显示*/
    public void setRedDot(Boolean isShow ){
    	if( isShow ){
    		red_dot.setVisibility(View.VISIBLE);
    	}else{
    		red_dot.setVisibility(View.GONE);
    	}
    }
}

布局文件main.xml
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" >
            <!-- TabHost控件->TabWidget(必须命名为tabs)->FrameLayout(必须命名为tabcontent)-->
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="fill_parent"
                android:layout_height="0.0dip"
                android:layout_weight="1.0" />
            
			<!-- tab的导航按钮,此处隐藏了 -->
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_weight="0.0"
                android:visibility="gone" />

            <LinearLayout
                android:id="@+id/main_tab_group"
                android:layout_width="fill_parent"
                android:layout_height="@dimen/mainTabs_bar_height"
                android:orientation="horizontal" >

                <FrameLayout
                    android:layout_width="0.0dip"
                    android:layout_height="fill_parent"
                    android:layout_weight="1.0"
                    android:background="@null" >

                    <LinearLayout
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="bottom|center" >

                        <RadioButton
                            android:id="@+id/mainTabs_radio1"
                            android:layout_width="fill_parent"
                            android:layout_height="fill_parent"
                            android:layout_weight="1"
                            android:background="@android:color/white"
                            android:button="@null"
                            android:drawableTop="@drawable/tabbar_icon_project"
                            android:gravity="center"
                            android:paddingTop="5dp"
                            android:text="选项1"
                            android:textColor="@drawable/tabhost_text_color"
                            android:textSize="@dimen/mainTabs_radio_btn_textSize" />
                    </LinearLayout>
                    <LinearLayout
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="center"
                        android:paddingLeft="35dp"
                        android:paddingBottom="38dp">

                        <View
                            android:id="@+id/mainTabs_red_dot1"
                            android:layout_width="@dimen/indivdual_red_dot_size"
                            android:layout_height="@dimen/indivdual_red_dot_size"
                            android:background="@drawable/indivdual_red_dot"
                            android:visibility="invisible" />
                    </LinearLayout>
                </FrameLayout>

                <FrameLayout
                    android:layout_width="0.0dip"
                    android:layout_height="fill_parent"
                    android:layout_weight="1.0"
                    android:background="@null" >

                    <LinearLayout
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="bottom|center" >

                        <RadioButton
                            android:id="@+id/mainTabs_radio2"
                            android:layout_width="fill_parent"
                            android:layout_height="fill_parent"
                            android:layout_weight="1"
                            android:background="@android:color/white"
                            android:button="@null"
                            android:drawableTop="@drawable/tabbar_icon_project"
                            android:gravity="center"
                            android:paddingTop="5dp"
                            android:text="选项2"
                            android:textColor="@drawable/tabhost_text_color"
                            android:textSize="@dimen/mainTabs_radio_btn_textSize" />
                    </LinearLayout>
                </FrameLayout>

                <FrameLayout
                    android:layout_width="0.0dip"
                    android:layout_height="fill_parent"
                    android:layout_weight="1.0"
                    android:background="@null" >

                    <LinearLayout
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="bottom|center" >

                        <RadioButton
                            android:id="@+id/mainTabs_radio3"
                            android:layout_width="fill_parent"
                            android:layout_height="fill_parent"
                            android:layout_weight="1"
                            android:background="@android:color/white"
                            android:button="@null"
                            android:drawableTop="@drawable/tabbar_icon_project"
                            android:gravity="center"
                            android:paddingTop="5dp"
                            android:text="选项3"
                            android:textColor="@drawable/tabhost_text_color"
                            android:textSize="@dimen/mainTabs_radio_btn_textSize" />
                    </LinearLayout>

                    
                </FrameLayout>

                <FrameLayout
                    android:layout_width="0.0dip"
                    android:layout_height="fill_parent"
                    android:layout_weight="1.0"
                    android:background="@null" >

                    <LinearLayout
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="bottom|center" >

                        <RadioButton
                            android:id="@+id/mainTabs_radio4"
                            android:layout_width="fill_parent"
                            android:layout_height="fill_parent"
                            android:layout_weight="1"
                            android:background="@android:color/white"
                            android:button="@null"
                            android:drawableTop="@drawable/tabbar_icon_more"
                            android:gravity="center"
                            android:paddingTop="5dp"
                            android:text="更多"
                            android:textColor="@drawable/tabhost_text_color"
                            android:textSize="@dimen/mainTabs_radio_btn_textSize" />
                    </LinearLayout>
                </FrameLayout>
            </LinearLayout>
        </LinearLayout>
    </FrameLayout>

</TabHost>
demo地址: http://download.csdn.net/download/duduhali/9413239

下面是极客学院上的一个demo,是使用tabHost做的选项卡界面的多功能时钟案例

四 使用tabHost做的选项卡界面的多功能时钟案例


这是出自极客学院的一个demo,我自己做了一些改动,粘出一个最关键的几个类和一个布局文件

主界面 MainActivity.java
public class MainActivity extends Activity {
	private StopWatchView stopWatchView;
    private TabHost tabHost;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tabHost = (TabHost) findViewById(android.R.id.tabhost);
        tabHost.setup();
        //newTabSpec(String tag)设置tag标记,可用tabHost.setCurrentTabByTag(tag)设置显示页,
        //setIndicator(String title)设置导航按钮上的文字,TabWidget隐藏时不起作用
        tabHost.addTab(tabHost.newTabSpec("tabTime").setIndicator("时钟").setContent(R.id.tabClock));
        tabHost.addTab(tabHost.newTabSpec("tabAlarm").setIndicator("闹钟").setContent(R.id.tabAlarm));
        tabHost.addTab(tabHost.newTabSpec("tabTimer").setIndicator("计时器").setContent(R.id.tabTimer));
        tabHost.addTab(tabHost.newTabSpec("tabStopWatch").setIndicator("秒表").setContent(R.id.tabStopWatch));
        
        stopWatchView = (StopWatchView) findViewById(R.id.tabStopWatch);
        
    }
    @Override
    protected void onDestroy() {
    	stopWatchView.onDestory();
    	super.onDestroy();
    }
}

主界面布局 activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >
			<!-- TabHost控件->TabWidget(必须命名为tabs)->FrameLayout(必须命名为tabcontent)-->
			<!-- tab的导航按钮,可在FrameLayout之上,也可在其下 -->
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
            </TabWidget>

            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent" >

                <com.duduhali.myclock.ClockView
                    android:id="@+id/tabClock"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical" >

                    <TextView
                        android:id="@+id/tvTime"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:gravity="center"
                        android:textAppearance="?android:attr/textAppearanceLarge" />
                </com.duduhali.myclock.ClockView>

                <com.duduhali.myclock.AlarmView
                    android:id="@+id/tabAlarm"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical" >

                    <ListView
                        android:id="@+id/lvAlarmList"
                        android:layout_width="fill_parent"
                        android:layout_height="0dp"
                        android:layout_weight="1" >
                    </ListView>

                    <Button
                        android:id="@+id/btnAddAlarm"
                        android:layout_width="fill_parent"
                        android:layout_height="wrap_content"
                        android:text="Add alarm" />
                </com.duduhali.myclock.AlarmView>

                <com.duduhali.myclock.TimerView
                    android:id="@+id/tabTimer"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical" >
                </com.duduhali.myclock.TimerView>

                <com.duduhali.myclock.StopWatchView
                    android:id="@+id/tabStopWatch"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical" >
                </com.duduhali.myclock.StopWatchView>
            </FrameLayout>
        </LinearLayout>
    </TabHost>

</FrameLayout>

下边是选项卡的四个选项类文件

ClockView.java
/** 时钟 */
public class ClockView extends LinearLayout {
	private TextView tvTime;
	public ClockView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public ClockView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public ClockView(Context context) {
		super(context);
	}

	//当加载完成xml后,就会执行此方法
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		
		tvTime = (TextView) findViewById(R.id.tvTime);
		tvTime.setText("Hello");
		
		timerHandler.sendEmptyMessage(0);
		
	}
	
	//当窗口中包含的可见的view发生变化时触发
	//可见时用Handler更新时间,不可见时停止更新
	@Override
	protected void onVisibilityChanged(View changedView, int visibility) {
		super.onVisibilityChanged(changedView, visibility);
		
		if (visibility==View.VISIBLE) {
			timerHandler.sendEmptyMessage(0);
		}else{
			timerHandler.removeMessages(0);
		}
	}
	/**获取本地时间并格式化显示*/
	private void refreshTime(){
		Calendar c = Calendar.getInstance();
		tvTime.setText(String.format("%d:%d:%d", c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE),c.get(Calendar.SECOND)));
	}
	
	private Handler timerHandler = new Handler(){
		
		public void handleMessage(android.os.Message msg) {
			refreshTime();

			if (getVisibility()==View.VISIBLE) {
				//发送延时消息,形成循环
				timerHandler.sendEmptyMessageDelayed(0, 1000);
			}
		};
	};
}

AlarmView.java
/** 闹铃 */
public class AlarmView extends LinearLayout {
	private Button btnAddAlarm;
	private ListView lvAlarmList;
	/**SharedPreferences存储时的key值*/
	private static final String KEY_ALARM_LIST = "alarmList";
	private ArrayAdapter<AlarmData> adapter;
	/**Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent*/
	private AlarmManager alarmManager;
	
	public AlarmView(Context context, AttributeSet attrs) {
		super(context, attrs);
		alarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
	}
	//当加载完成xml后,就会执行此方法
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();

		btnAddAlarm = (Button) findViewById(R.id.btnAddAlarm);
		lvAlarmList = (ListView) findViewById(R.id.lvAlarmList);
		adapter = new ArrayAdapter<AlarmView.AlarmData>(getContext(), android.R.layout.simple_list_item_1);
		lvAlarmList.setAdapter(adapter);
		readSavedAlarmList();
		btnAddAlarm.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				addAlarm();
			}
		});

		lvAlarmList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
			@Override
			public boolean onItemLongClick(AdapterView<?> parent, View view,
					final int position, long id) {
				
//				并用Builder方法形成了一个对象链
//				new AlertDialog.Builder(self) 
//				.setTitle("请输入") 
//				.setIcon(android.R.drawable.ic_dialog_info) 
//				.setView(new EditText(self)) 
//				.setPositiveButton("确定", null) 
//				.setNegativeButton("取消", null) 
//				.show(); 
				new AlertDialog.Builder(getContext())
				.setTitle("是否删除")
				.setItems(new CharSequence[]{"删除"}, new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						switch (which) {
						case 0:
							deleteAlarm(position);
							break;
						default:
							break;
						}
					}
				})
				.setNegativeButton("取消", null).show();
				return true;
			}
		});
	}
	/**从列表中删除一项数据并取消相应的PendingIntent对象,更新本地存储的数据*/
	private void deleteAlarm(int position){
		AlarmData ad = adapter.getItem(position);
		adapter.remove(ad);
		saveAlarmList();
		//删除PendingIntent
		alarmManager.cancel(
				PendingIntent.getBroadcast(getContext(), ad.getId(),
						new Intent(getContext(), AlarmReceiver.class), 0)
				);
	}
	/**时间选择器监听器*/
	class MyTimeSetListener implements OnTimeSetListener{
		@Override
		public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
			Calendar calendar = Calendar.getInstance();
			calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
			calendar.set(Calendar.MINUTE, minute);
			calendar.set(Calendar.SECOND, 0);
			calendar.set(Calendar.MILLISECOND, 0);
			Calendar currentTime = Calendar.getInstance();
			if (calendar.getTimeInMillis()<=currentTime.getTimeInMillis()) {
				calendar.setTimeInMillis(calendar.getTimeInMillis()+24*60*60*1000);
			}
			AlarmData ad = new AlarmData(calendar.getTimeInMillis());
			adapter.add(ad);
			
			//AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
			//setRepeating()在API 19(即Kitkat)之后,这一方法将不再准确地保证每次工作都在你设置的时间开始
			//该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,
			//第三个参数表示闹钟两次执行的间隔时间(不设置第三个参数为一次性闹铃),第四个参数表示闹钟响应动作。
			alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, ad.getTime(), 5*60*1000, 
					PendingIntent.getBroadcast(getContext(), ad.getId(),
							new Intent(getContext(), AlarmReceiver.class), 0));
/*	Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel删除
	Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效,PendingIntent自带Context,而Intent需要在某个Context内运行
	Intent在原task中运行,PendingIntent在新的task中运行
在PendingIntent.java文件中,我们可以看到有如下几个比较常见的静态函数:
	public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
	public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
	public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
	public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags)
	public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags, Bundle options)
getActivity()获取一个PendingIntent对象,该对象日后激发时做的事情是启动一个新activity。
当它异步激发时,会执行类似Context.startActivity()那样的动作。
相应地,getBroadcast()和getService()所获取的PendingIntent对象在激发时,
会分别执行类似Context..sendBroadcast()和Context.startService()这样的动作。
至于最后两个getActivities(),用得比较少,激发时可以启动几个activity。*/
			saveAlarmList();
		}
	}
	/**弹出时间选择对话框*/
	private void addAlarm(){
		//TODO 
		Calendar c = Calendar.getInstance();
		/*通过new关键字创建DatePickerDialog、TimePickerDialog实例,调用它们的show()方法即可将日期选择对话框、时间选择对话框显示出来。
		 * 为DatePickerDialog、TimePickerDialog绑定监听器,这样可以保证用户通过DatePickerDialog、TimePickerDialog
		 * 设置事件是触发监听器,从而通过监听器来获取用户设置的事件。
		 * 参数:
		 * Context 运行组件的Activity。
		 * callBack 用户选择好时间后,通知应用的回调函数。
		 * hourOfDay 初始的小时。
		 * Minute 初始的分钟。
		 * is24HourView 是否使用24小时制。*/
		new TimePickerDialog(getContext(), new MyTimeSetListener(), c.get(Calendar.HOUR_OF_DAY), 
				c.get(Calendar.MINUTE), true){
			@Override
			protected void onStop() {
				//super.onStop(); 重载onStop方法,取消调用父类的onStop方法,不然时间选择对话框监听器会执行两次
			}
		}.show();
	}
	/**把列表数据拼接成一个字符串用SharedPreferences存储在本地*/
	private void saveAlarmList(){
		Editor editor = getContext().getSharedPreferences(AlarmView.class.getName(), Context.MODE_PRIVATE).edit();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < adapter.getCount(); i++) {
			sb.append(adapter.getItem(i).getTime()).append(",");
		}
		if (sb.length()>1) {
			String content = sb.toString().substring(0, sb.length()-1);
			editor.putString(KEY_ALARM_LIST, content);
		}else{
			editor.putString(KEY_ALARM_LIST, null);
		}
		editor.commit();
	}

	/**用SharedPreferences读取存储在本地数据,添加到列表中*/
	private void readSavedAlarmList(){
		SharedPreferences sp = getContext().getSharedPreferences(AlarmView.class.getName(),
				Context.MODE_PRIVATE);
		String content = sp.getString(KEY_ALARM_LIST, null);
		if (content!=null) {
			String[] timeStrings = content.split(",");
			for (String string : timeStrings) {
				adapter.add(new AlarmData(Long.parseLong(string)));
			}
		}
	}

	/**分装了时间日期的数据结构*/
	private static class AlarmData{
		
		private String timeLabel="";
		private long time = 0;
		private Calendar date;
		
		public AlarmData(long time) {
			this.time = time;

			date = Calendar.getInstance();
			date.setTimeInMillis(time);

			timeLabel = String.format("%d月%d日  %d:%d", 
					date.get(Calendar.MONTH)+1,
					date.get(Calendar.DAY_OF_MONTH),
					date.get(Calendar.HOUR_OF_DAY),
					date.get(Calendar.MINUTE));
		}
		public long getTime() {
			return time;
		}
		public String getTimeLabel() {
			return timeLabel;
		}
		@Override
		public String toString() {
			return getTimeLabel();
		}
		/**时间做ID,把long转化为int使精确度只到分钟*/
		public int getId(){
			return (int)(getTime()/1000/60);
		}
	}
}

TimerView.java
/**计时器*/
public class TimerView extends LinearLayout implements OnClickListener{
	private static final int MSG_WHAT_TIME_IS_UP = 1;
	private static final int MSG_WHAT_TIME_TICK = 2;
	
	private int allTimerCount = 0;
	/**调度器*/
	private Timer timer=new Timer();
	/**被调度的类,TimerTask对时间进行计算由Handler更新界面*/
	private TimerTask timerTask = null;
	private Button btnStart,btnPause,btnResume,btnReset;
	private EditText etHour,etMin,etSec;
	public TimerView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	//当加载完成xml后,就会执行此方法
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		View layout = LayoutInflater.from(getContext()).inflate(R.layout.timer_view,null);
		
		btnPause = (Button) layout.findViewById(R.id.btnPause);
		btnPause.setOnClickListener(this);
		btnReset = (Button) layout.findViewById(R.id.btnReset);
		btnReset.setOnClickListener(this);
		btnResume = (Button) layout.findViewById(R.id.btnResume);
		btnResume.setOnClickListener(this);
		btnStart = (Button) layout.findViewById(R.id.btnStart);
		btnStart.setOnClickListener(this);
		
		etHour = (EditText) layout.findViewById(R.id.etHour);
		etMin = (EditText) layout.findViewById(R.id.etMin);
		etSec = (EditText) layout.findViewById(R.id.etSec);
		etHour.setText("00");
		etHour.addTextChangedListener(new MyTextWatchar(etHour));
		etMin.setText("00");
		etMin.addTextChangedListener( new MyTextWatchar(etMin));
		etSec.setText("00");
		etSec.addTextChangedListener( new MyTextWatchar(etSec));

		btnStart.setVisibility(View.VISIBLE);
		btnStart.setEnabled(false);
		btnPause.setVisibility(View.GONE);
		btnResume.setVisibility(View.GONE);
		btnReset.setVisibility(View.GONE);
		this.removeAllViews();
		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		layout.setLayoutParams(lp);//设置使能占满父布局
		this.addView(layout);//把view加入布局
	}
	
	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		switch (arg0.getId()) {
		case R.id.btnPause:
			stopTimer();
			btnPause.setVisibility(View.GONE);
			btnResume.setVisibility(View.VISIBLE);
			break;
		case R.id.btnReset:
			stopTimer();
			etHour.setText("0");
			etMin.setText("0");
			etSec.setText("0");
			btnReset.setVisibility(View.GONE);
			btnResume.setVisibility(View.GONE);
			btnPause.setVisibility(View.GONE);
			btnStart.setVisibility(View.VISIBLE);
			break;
		case R.id.btnResume:
			startTimer();
			btnResume.setVisibility(View.GONE);
			btnPause.setVisibility(View.VISIBLE);
			break;
		case R.id.btnStart:
			startTimer();
			btnStart.setVisibility(View.GONE);
			btnPause.setVisibility(View.VISIBLE);
			btnReset.setVisibility(View.VISIBLE);
	break;
		default:
			break;
		}
	}
	
	/**自定义编辑框监听器*/
	class MyTextWatchar implements TextWatcher{
		EditText et;
		public MyTextWatchar(EditText et) {
			// TODO Auto-generated constructor stub
			this.et = et;
		}
		@Override
		public void afterTextChanged(Editable arg0) {//表示最终内容
			// TODO Auto-generated method stub
		}
	            
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			//四个参数分别为:变化之前的内容,开始的位置,被改变的旧内容数,改变后的内容数量
			//这里的s表示改变之前的内容,通常start和count组合,可以在s中读取本次改变字段中被改变的内容。而after表示改变后新的内容的数量。
		}
		@Override
		 public void onTextChanged(CharSequence s, int start, int before, int count) {
			//四个参数分别为:变化之后的内容,开始的位置,改变前的内容数量,改变后的内容数量
            //通常start和count组合,可以在s中读取本次改变字段中新的内容。而before表示被改变的内容的数量。
			if (!TextUtils.isEmpty(s)) {
				int value = Integer.parseInt(s.toString());
				if (value>59) {
					et.setText("59");
				}else if (value<0) {
					et.setText("0");
				}
			}
			checkToEnableBtnStart();
		}
	}
	/**检测输入是否有效,有效时设置开始按钮可点击*/
	private void checkToEnableBtnStart(){
		btnStart.setEnabled((!TextUtils.isEmpty(etHour.getText())&&Integer.parseInt(etHour.getText().toString())>0)||
				(!TextUtils.isEmpty(etMin.getText())&&Integer.parseInt(etMin.getText().toString())>0)||
				(!TextUtils.isEmpty(etSec.getText())&&Integer.parseInt(etSec.getText().toString())>0));
	}
	
	/**开始计时*/
	private void startTimer(){
		if (timerTask==null) {
			allTimerCount = Integer.parseInt(etHour.getText().toString())*60*60+Integer.parseInt(etMin.getText().toString())*60+Integer.parseInt(etSec.getText().toString());
			timerTask = new TimerTask() {
				@Override
				public void run() {
					allTimerCount--;
					handler.sendEmptyMessage(MSG_WHAT_TIME_TICK);
					if (allTimerCount<=0) {
						handler.sendEmptyMessage(MSG_WHAT_TIME_IS_UP);
						stopTimer();
					}
				}
			};
			//开始一个定时任务
			timer.schedule(timerTask, 500, 1000);//0.5秒后开始执行,每1秒执行一次
		}
	}
	/**停止计时*/
	private void stopTimer(){
		if (timerTask!=null) {
			//停止计时任务
			timerTask.cancel();
			timerTask = null;
		}
	}
	/**更新界面,Timer的线程中,无法直接操作UI线程*/
	private Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case MSG_WHAT_TIME_TICK:
				//计时进行中,根据时间更新界面
				int hour = allTimerCount/60/60;
				int min = (allTimerCount/60)%60;
				int sec = allTimerCount%60;
				
				etHour.setText(hour+"");
				etMin.setText(min+"");
				etSec.setText(sec+"");
				
				break;
			case MSG_WHAT_TIME_IS_UP:
				//计时结束,重置界面
				new AlertDialog.Builder(getContext()).setTitle("Time is up").setMessage("Time is up").setNegativeButton("Cancel", null).show();
				
				btnReset.setVisibility(View.GONE);
				btnResume.setVisibility(View.GONE);
				btnPause.setVisibility(View.GONE);
				btnStart.setVisibility(View.VISIBLE);
				
				break;
			default:
				break;
			}
		};
	};
	
}

StopWatchView.java
/**秒表*/
public class StopWatchView extends LinearLayout implements OnClickListener{
	private static final int MSG_WHAT_SHOW_TIME = 1;
	private int tenMSecs = 0;
	/**调度器*/
	private Timer timer = new Timer();
	/**被调度的类,用来计时*/
	private TimerTask timerTask = null;
	/**被调度的类,用来更新界面,借助Handler*/
	private TimerTask showTimeTask = null;

	private TextView tvHour,tvMin,tvSec,tvMSec;
	private Button btnStart,btnResume,btnReset,btnPause,btnLap;
	private ListView lvTimeList;
	private ArrayAdapter<String> adapter;
	public StopWatchView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	//当加载完成xml后,就会执行此方法
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		View layout = LayoutInflater.from(getContext()).inflate(R.layout.stop_watch_view,null);
		tvHour = (TextView) layout.findViewById(R.id.timeHour);
		tvHour.setText("0");
		tvMin = (TextView) layout.findViewById(R.id.timeMin);
		tvMin.setText("0");
		tvSec = (TextView) layout.findViewById(R.id.timeSec);
		tvSec.setText("0");
		tvMSec = (TextView) layout.findViewById(R.id.timeMSec);
		tvMSec.setText("0");
		
		btnLap = (Button) layout.findViewById(R.id.btnSWLap);
		btnLap.setOnClickListener(this);
		btnPause = (Button) layout.findViewById(R.id.btnSWPause);
		btnPause.setOnClickListener(this);
		btnReset = (Button) layout.findViewById(R.id.btnSWReset);
		btnReset.setOnClickListener(this);
		btnResume = (Button) layout.findViewById(R.id.btnSWResume);
		btnResume.setOnClickListener(this);
		btnStart = (Button) layout.findViewById(R.id.btnSWStart);
		btnStart.setOnClickListener(this);
		
		btnLap.setVisibility(View.GONE);
		btnPause.setVisibility(View.GONE);
		btnReset.setVisibility(View.GONE);
		btnResume.setVisibility(View.GONE);
		
		lvTimeList=(ListView) layout.findViewById(R.id.lvWatchTimeList);
		adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1);
		lvTimeList.setAdapter(adapter);
		
		showTimeTask = new TimerTask() {
			@Override
			public void run() {
				hander.sendEmptyMessage(MSG_WHAT_SHOW_TIME);
			}
		};
		//开始定时任务
		timer.schedule(showTimeTask, 200, 200);//200毫秒后开始执行,每200毫秒执行一次
		
		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		layout.setLayoutParams(lp);//设置使能占满父布局
		this.addView(layout);
	}
	
	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		switch (arg0.getId()) {
		case R.id.btnSWLap:
			adapter.insert(String.format("%d:%d:%d.%d", tenMSecs/100/60/60,tenMSecs/100/60%60,tenMSecs/100%60,tenMSecs%100), 0);
			break;
		case R.id.btnSWPause:
			stopTimer();
			btnPause.setVisibility(View.GONE);
			btnResume.setVisibility(View.VISIBLE);
			btnLap.setVisibility(View.GONE);
			btnReset.setVisibility(View.VISIBLE);
			break;
		case R.id.btnSWReset:
			stopTimer();
			tenMSecs = 0;
			adapter.clear();
			btnLap.setVisibility(View.GONE);
			btnPause.setVisibility(View.GONE);
			btnReset.setVisibility(View.GONE);
			btnResume.setVisibility(View.GONE);
			btnStart.setVisibility(View.VISIBLE);
			break;
		case R.id.btnSWResume:
			startTimer();
			btnResume.setVisibility(View.GONE);
			btnPause.setVisibility(View.VISIBLE);
			btnReset.setVisibility(View.GONE);
			btnLap.setVisibility(View.VISIBLE);
			break;
		case R.id.btnSWStart:
			startTimer();
			btnStart.setVisibility(View.GONE);
			btnPause.setVisibility(View.VISIBLE);
			btnLap.setVisibility(View.VISIBLE);
			break;
		default:
			break;
		}
	}
	/**开始计时*/
	private void startTimer(){
		if (timerTask==null) {
			timerTask = new TimerTask() {
				@Override
				public void run() {
					tenMSecs++;
				}
			};
			//开始定时任务
			timer.schedule(timerTask, 10, 10);//10毫秒后开始执行,每10毫秒执行一次
		}
	}
	/**停止计时*/
	private void stopTimer(){
		if (timerTask!=null) {
			//停止计时任务
			timerTask.cancel();
			timerTask=null;
		}
	}
	/**更新界面,Timer的线程中,无法直接操作UI线程*/
	private Handler hander = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case MSG_WHAT_SHOW_TIME:
				tvHour.setText(tenMSecs/100/60/60+"");
				tvMin.setText(tenMSecs/100/60%60+"");
				tvSec.setText(tenMSecs/100%60+"");
				tvMSec.setText(tenMSecs%100+"");
				break;
			default:
				break;
			}
		};
	};
	
	public void onDestory() {
		//退出计时器
		timer.cancel();
	}
}

还有一个启动闹铃的服务 AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		
		AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		//删除异步激发任务
		am.cancel(PendingIntent.getBroadcast(context, getResultCode(), 
				new Intent(context, AlarmReceiver.class), 0));
		//启动音乐播放界面
		Intent i = new Intent(context, PlayAlarmAty.class);
		i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		context.startActivity(i);
	}

}

demo地址: http://download.csdn.net/download/duduhali/9413290



源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值