Fragment的基本应用
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/50933621
Fragment
还是先来基本介绍。
Fragment –> 片段。
在Android3.0的时候被引入,它的出现主要是给大屏幕设备提供更加灵活的UI支持。通过对Activity布局进行分片。更加方便的对每块进行独立控制。这些片段能够被不同的activity复用。
fragment生命周期
每一个fragment拥有自己的生命周期,可是fragment要依赖于activity存在,生命周期受到包含它的activity的生命周期控制。
来两张神图~~
左图就是fragment的生命周期图。右图是fragment与activity各自生命周期的对比。
介绍一下经常使用的几个生命周期函数:
onAttach(Context) –> 当fragment被绑定到activity上时回调
onCreate(Bundle) –> 当fragment对象创建的时候回调。一般在此方法里做參数接收。
onCreateView(LayoutInflater, ViewGroup, Bundle) –> 创建fragment视图时回调
onDestoryView –> 视图销毁时回调
onDestory –> 销毁fragment时回调
onDetech() –> fragment与activity失去关联时回调
fragment的使用
使用fragment能够当成一个控件,直接放到activity布局文件中。也能够在代码里面动态的加入、更新或者删除。
以下的activity布局文件中定义了一个fragment和一个frameLayout。
使用标签能够称之为静态的Fragment。在activity创建的时候也会去创建并显示它,而framelayout是一个容器,我们在代码中能够动态的加入一个fragment进去。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".fragments.ArticleActivity">
<!--headlines-->
<fragment
android:id="@+id/headline_fragment"
android:name="com.jacksen.demo.view.fragments.HeadlinesFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@layout/fragment_item_list" />
<!--article-->
<FrameLayout
android:id="@+id/article_frame_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
注意:
使用标签显示Fragment的时候,须要对这个fragment设置一个id或者tag。否则会出现”Error inflating class fragment”错误。
Caused by: java.lang.IllegalArgumentException: Binary XML file line #10: Must specify unique android:id, android:tag, or have a parent with an id for com.jacksen.demo.view.fragments.HeadlinesFragment
Fragment的子类继承的时候。假设你的minSdkVersion <= 11,须要引入V4包,然后倒入android.support.v4.app.Fragment包。
假设是大于11。直接导入android.app.Fragment包就可以。
package com.jacksen.demo.view.fragments;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.jacksen.demo.view.R;
import com.jacksen.demo.view.fragments.dummy.ArticleBean;
import com.jacksen.demo.view.fragments.dummy.ArticleBean.ArticleItem;
public class HeadlinesFragment extends Fragment {
private static final String ARG_COLUMN_COUNT = "column-count";
private int mColumnCount = 2;
private OnChangeArticleListener mListener;
public HeadlinesFragment() {
}
public static HeadlinesFragment newInstance(int columnCount) {
HeadlinesFragment fragment = new HeadlinesFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d("HeadlinesFragment", "onAttach");
if (context instanceof OnChangeArticleListener) {
mListener = (OnChangeArticleListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnChangeArticleListener");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("HeadlinesFragment", "onCreate");
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("HeadlinesFragment", "onCreateView");
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new HeadlinesRecyclerViewAdapter(ArticleBean.ITEMS, mListener));
}
return view;
}
@Override
public void onDetach() {
super.onDetach();
Log.d("HeadlinesFragment", "onDetach");
mListener = null;
}
public interface OnChangeArticleListener {
void onChangeArticle(ArticleItem item);
}
}
动态的加入fragment就须要在代码里面通过FragmentManager等类进行操作:
frameLayout = (FrameLayout) findViewById(R.id.article_frame_layout);
fragmentManager = getSupportFragmentManager();
articleFragment = ArticleFragment.newInstance();
fragmentManager.beginTransaction().replace(R.id.article_frame_layout, articleFragment).commit();
Fragment的事务管理
android里通过FragmentManager类进行管理fragment。一组对fragment的操作(加入、删除、替换等)称为一个事务,通过FragmentTransaction类来提交运行。
也能够把事务加入到回退栈中,进行回滚。这有点相似于数据库的事务机制。
注意:
事物操作最后必须调用commit()才干运行。
调用commit()方法之后,也不是立马运行;假设须要立马运行,能够使用executePendingTransactions()方法。
一次性add多个fragment,显示的是最后一个。
任务栈回退针对的是事务。而不是fragment。一次事务操作过程中能够有非常多个对fragment的操作。
仅仅能在activity处于可保存状态的情况下。进行事务操作。
否则引发例如以下异常:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
比方。在onPause()或者onStop()中提交事务,就会出现以上问题,假设非要在这些生命周期里面进行事务提交。请使用FragmentTransaction类的commitAllowingStateLoss()方法,同意状态丢失。
- 假设activity继承的是AppCompatActivity。onBackPressed()回调函数里面是利用的V4包的getSupportFragmentManager()进行的栈回退,所以做fragment回退的时候须要注意引用的是不是V4包的Fragment类。
/**
* Take care of popping the fragment back stack or finishing the activity
* as appropriate.
*/
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
supportFinishAfterTransition();
}
}
Fragment之间的切换
Fragment的切换就是基本就是利用add()、hide()、show()、replace()这四个方法。
情况一:採用add方式切换fragment
activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_fragments2);
ButterKnife.bind(this);
FragmentManager fragmentManager = getSupportFragmentManager();
TabFragment1 tabFragment1 = TabFragment1.newInstance();
fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment1).addToBackStack("tab1").commit();
}
TabFragment1代码:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment1", "onCreateView");
View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
ButterKnife.bind(this, view);
openNextBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager();
TabFragment2 tabFragment2 = TabFragment2.newInstance();
fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
}
});
return view;
}
TabFragment2代码:
/**
* Tab Fragment 2
*/
public class TabFragment2 extends Fragment {
public TabFragment2() {
}
public static TabFragment2 newInstance() {
TabFragment2 fragment = new TabFragment2();
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d("TabFragment2", "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TabFragment2", "onCreate");
if (getArguments() != null) {
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment2", "onCreateView");
return inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d("TabFragment2", "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("TabFragment2", "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d("TabFragment2", "onDetach");
}
}
此时都是採用add的方式进行显示fragment。每次都会把加入fragment的事务叠加到回退栈上面。
在TabFragment1界面点击button加入TabFragment2界面,然后在按返回键回退,打印出生命周期例如以下:
情况二:採用replace的方式切换fragment
activity代码不变。
TabFragment1的代码例如以下:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment1", "onCreateView");
View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
ButterKnife.bind(this, view);
openNextBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager();
TabFragment2 tabFragment2 = TabFragment2.newInstance();
fragmentManager.beginTransaction().replace(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
}
});
return view;
}
TabFragment1切换到TabFragment2的时候,使用的replace方法。replace方法的作用是remove掉全部加入到同样id的容器里的fragment。然后加入參数里的fragment。所以会回调TabFragment1的onDestoryView()方法,等到从TabFragment2返回的时候,去运行replace事务的相反操作,也就会又一次创建TabFragment1的视图,回调onCreateView()方法。
remove:
我们在TabFragment2界面中调用remove方法,把TabFragment1移除掉。
TabFragment1.java:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment1", "onCreateView");
View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
ButterKnife.bind(this, view);
openNextBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager();
TabFragment2 tabFragment2 = TabFragment2.newInstance();
fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
}
});
return view;
}
TabFragment2.java:
private onBtnClickListener onBtnClickListener;
public TabFragment2() {
}
public static TabFragment2 newInstance() {
TabFragment2 fragment = new TabFragment2();
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d("TabFragment2", "onAttach");
if (context instanceof onBtnClickListener){
onBtnClickListener = (TabFragment2.onBtnClickListener) context;
}else{
throw new RuntimeException(context.toString()
+ " must implement onBtnClickListener");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TabFragment2", "onCreate");
if (getArguments() != null) {
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment2", "onCreateView");
View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
ButterKnife.bind(this, view);
removeFragmentBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBtnClickListener.removeFragment1();
}
});
return view;
}
public interface onBtnClickListener{
void removeFragment1();
}
activity:
@Override
public void removeFragment1() {
fragmentManager.beginTransaction().remove(tabFragment1).commit();
}
从代码里看到,remove操作的事务没有加入到回退栈中。所以从TabFragment2中返回的时候,直接退到了activity界面。
可是从界面来看,从TabFragment2会退到activity之后。再次按返回键并没有退出activity,然后再按返回键的时候才会退出activity。原因就是。尽管remove了TabFragment1,可是仅仅是回调了onDestoryView()方法销毁了视图,此时TabFragment1的对象资源和与activity的关联还没有断开。所以点击返回键的时候会有一个没有“响应”。
以下看看我们把reomve()操作加入到回退栈的情况:
@Override
public void removeFragment1() {
fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack("remove").commit();
}
与上面的情况相比,多了TabFragment1的onCreateView()这一步。这是由于把remove事务加入到了任务栈,回退的时候逆向运行该操作。
使用replace()方法和remove()方法会导致视图销毁,所以。切换fragment的时候,假设须要视图保留视图。就不能用这两个方法。
Fragment与Activity之间传值
fragment –> activity 99%的做法都是通过接口回调来做的。定义一个接口。activity中实现此接口方法,在fragment里面调用接口方法
activity –> fragment 通过findFragmentById()后者findFragmentByTag()方法获取fragment实例调用fragment里的public方法就可以。
接着往下看~~
TabFragment2调用接口方法:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("TabFragment2", "onCreateView");
View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
ButterKnife.bind(this, view);
removeFragmentBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBtnClickListener.removeFragment1();
}
});
tellSthBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBtnClickListener.tellSth("这是我要对你说的话!
");
}
});
return view;
}
/**
* 回调接口
*/
public interface onBtnClickListener {
void removeFragment1();
void tellSth(String str);
}
activity实现该接口的方法:
public class TestFragments extends AppCompatActivity implements TabFragment2.onBtnClickListener {
@Bind(R.id.test_fragments_layout)
FrameLayout testFragmentsLayout;
private FragmentManager fragmentManager;
private TabFragment1 tabFragment1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_fragments2);
ButterKnife.bind(this);
fragmentManager = getSupportFragmentManager();
tabFragment1 = TabFragment1.newInstance();
fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment1, "tab1").addToBackStack("tab1").commit();
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
@Override
public void removeFragment1() {
fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack("remove").commit();
}
@Override
public void tellSth(String str) {
((TabFragment1)fragmentManager.findFragmentByTag("tab1")).showSth("hello: " + str);
}
}
在TabFragment1中定义方法供activity调用:
public void showSth(String str) {
getSthEt.setText(str);
}
Fragment与Fragment传值
activity给fragment传值你回了,fragment给activity传值你也会了。那么这个问题就不要问我了!。!
Fragment切换动画
Fragment的切换动画能够使用系统的标准动画,也能够自己定义动画。
使用系统的须要用到setTransition(),可是仅仅能设置系统提供的有限的动画效果。
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE
自己定义动画须要用到的类:
/**
* Set specific animation resources to run for the fragments that are
* entering and exiting in this transaction. These animations will not be
* played when popping the back stack.
*/
public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
@AnimRes int exit);
/**
* Set specific animation resources to run for the fragments that are
* entering and exiting in this transaction. The <code>popEnter</code>
* and <code>popExit</code> animations will be played for enter/exit
* operations specifically when popping the back stack.
*/
public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
@AnimRes int exit, @AnimRes int popEnter, @AnimRes int popExit);
注意:
setCustomAnimations()必须在add()、remove()、replace()调用之前设置。否则不起作用。
比方:有两个fragment A和B,从A切换到B的时候
@AnimRes int enter
表示Fragment B的进入动画@AnimRes int exit
表示Fragment A的退出动画@AnimRes int popEnter
表示当从B界面pop回到A时,Fragment A的进入动画@AnimRes int popExit
表示当从B界面pop回到A是,Fragment B的退出动画
假设使用add的方式显示下一个fragment。则仅仅会触发enter 和 popExit动画。由于这样的情况下A并没有被移除,仅仅是触发了与B相关的动画。
比方:
从TabFragment1使用add()方式显示TabFragment2:
FragmentManager fragmentManager = getFragmentManager();
TabFragment2 tabFragment2 = TabFragment2.newInstance();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_top, R.anim.slide_out_to_bottom);
fragmentTransaction.add(R.id.test_fragments_layout, tabFragment2, "tab2");
fragmentTransaction.addToBackStack("tab2");
fragmentTransaction.commit();
4个动画都是使用的简单的view动画,用属性动画能够做出更加绚丽的动画:
slide_in_from_right.xml
<?xml version="1.0" encoding="utf-8"?
>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="800"
android:fromXDelta="100.0%"
android:interpolator="@android:interpolator/accelerate_decelerate_interpolator"
android:toXDelta="0.0" />
slide_out_to_left.xml
<?xml version="1.0" encoding="utf-8"?
>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="800"
android:fromXDelta="0.0"
android:interpolator="@android:interpolator/accelerate_decelerate_interpolator"
android:toXDelta="-100%" />
slide_in_from_top.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromYDelta="-100.0%"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:toYDelta="0.0" />
slide_out_to_bottom.xml
<?xml version="1.0" encoding="utf-8"?
>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromYDelta="0"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:toYDelta="100%p" />
效果图:
假设採用的replace的方式,则会正常的触发4个动画。
FragmentManager fragmentManager = getFragmentManager();
TabFragment2 tabFragment2 = TabFragment2.newInstance();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_top, R.anim.slide_out_to_bottom);
fragmentTransaction.replace(R.id.test_fragments_layout, tabFragment2, "tab2");
fragmentTransaction.addToBackStack("tab2");
fragmentTransaction.commit();
这里仅仅介绍了setCustomAnimations()的使用方法,setTransition()方式的动画非常easy,就不介绍了。
本篇blog到此结束。
如有错误,欢迎留言。
谢谢~~~
Fragment的基本应用的更多相关文章
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 札记:Fragment基础
Fragment概述 在Fragment出现之前,Activity是app中界面的基本组成单位,值得一提的是,作为四大组件之一,它是需要"注册"的.组件的特性使得一个Activit ...
- EventBus实现activity跟fragment交互数据
最近老是听到技术群里面有人提出需求,activity跟fragment交互数据,或者从一个activity跳转到另外一个activity的fragment,所以我给大家介绍一个开源项目,EventBu ...
- Android:Activity+Fragment及它们之间的数据交换.
Android:Activity+Fragment及它们之间的数据交换 关于Fragment与Fragment.Activity通信的四种方式 比较好一点的Activity+Fragment及它们之间 ...
- Android中Fragment和ViewPager那点事儿(仿微信APP)
在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...
- Android开发学习—— Fragment
#Fragment* 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容* 生命周期方法跟Activity一致,可以理解把其为就是一个Activity* 定义布局文件作 ...
- Android中Fragment与Activity之间的交互(两种实现方式)
(未给Fragment的布局设置BackGound) 之前关于Android中Fragment的概念以及创建方式,我专门写了一篇博文<Android中Fragment的两种创建方式>,就如 ...
- Android中Fragment的两种创建方式
fragment是Activity中用户界面的一个行为或者是一部分.你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再使用.你可以认 ...
- Android Fragment 剖析
1.Fragment如何产生?2.什么是Fragment Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视.针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后 ...
- ILJMALL project过程中遇到Fragment嵌套问题:IllegalArgumentException: Binary XML file line #23: Duplicate id
出现场景:当点击"分类"再返回"首页"时,发生error退出 BUG描述:Caused by: java.lang.IllegalArgumentExcep ...
随机推荐
- Objective-C ,ios,iphone开发基础:UIAlertView使用详解
UIAlertView使用详解 Ios中为我们提供了一个用来弹出提示框的类 UIAlertView,他类似于javascript中的alert 和c#中的MessageBox(); UIAlertVi ...
- 【2017 Multi-University Training Contest - Team 5】Rikka with Graph
[Link]:http://acm.hdu.edu.cn/showproblem.php?pid=6090 [Description] 给你n个点; 让你在这n个点上最多连m条无向边; 使得 ∑ni= ...
- 简单的横向ListView实现(version 3.0)
版本号2仅仅是简单的实现了当手指按下的时候listView的Item向左移动一定的距离,并没有随着手指的左右移动而左右滚动.在这个版本号3.0中将会实现随着手指的移动而滚动的目标:当手指向左移动的时候 ...
- 漫话C++之string字符串类的使用(有汇编分析)
C++中并不提倡继续使用C风格的字符串,而是为字符串定义了专门的类,名为string. 使用前的准备工作 在使用string类型时,需要包含string头文件,且string位于std命名空间内: # ...
- 7.Linux 输入子系统分析
为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...
- C#解决关闭多线程的form主窗体时抛出ObjectDisposedException 异常
一.现象: 我在主窗体新建线程,使用子线程来处理接收到的数据,并且更新窗体显示内容,但关闭主窗体程序之后就程序就报错,如下所示: 二.分析问题: 由于新建线程的处理函数里边是一直死循环处理数据,虽然窗 ...
- 邮件协议与port
电子邮箱的协议有SMTP.POP2.POP3.IMAP4等.都隶属于TCP/IP协议簇,默认状态下.分别通过TCPport25.110和143建立连接.针对不同的用途和功能,我们在邮件se ...
- CSS外边距合并(塌陷/margin越界)
原文 简书原文:https://www.jianshu.com/p/5f18f12cd162 大纲 1.什么是外边距合并?(折叠外边距) 2.外边距带来的影响 3.折叠的结果 4.产生折叠的原因 5. ...
- 基于mpvue的小程序项目搭建的步骤
mpvue 是美团开源的一套语法与vue.js一致的.快速开发小程序的前端框架,按官网说可以达到小程序与H5界面使用一套代码.使用此框架,开发者将得到完整的 Vue.js 开发体验,同时为 H5 和小 ...
- windows CE项目开发
软件列表 1.Windows mobile 设备中心 2.Microsoft visual Studio 2008 3.串口调试工具(sscom42.exe) 4.Wince 6.0模拟器 5.vir ...