资讯专栏INFORMATION COLUMN

如何使用Android UI Fragment开发“列表-详情”界面

ormsf / 1845人阅读

摘要:在内获取返回结果有自己的和,但却不具有方法,但可以先取得托管它的,然后再返回数据。整理自安卓权威编程指南第章章。你可以从这里获取源码版权声明如何使用开发列表详情界面由在年月日写作。

在移动App里,有几种常见的界面形式:

手机上:一个列表界面A,点击某个条目后进入详情界面B,左右滑动可以切换到上/下条的详情界面;

平板上:由于屏幕足够大,列表界面A和详情界面B可以同时显示在屏幕上,分列两侧;

顶/底部若干标签,点击或者左右滑动可以显示不同的界面。

在Android上的解决方案之一是ViewPager + FragmentPagerAdapter + Fragment,在iOS上的解决方案之一是UICollectionView + UICollectionViewCell.

《安卓权威编程指南 Android Programming - The Big Nerd Ranch Guide》这本书的第7~12章、16~22章完成了一个功能复杂的APP开发,对于学习M(mode)V(view)C(controller)模式开发非常有益处,并且涉及非常多的主题:

如何创建并添加一个Fragment到Activity;

xml:样式style、主题theme、dp/sp、布局参数layout parameter、边距和内边距margin,padding;

使用ListFragment显示列表(介绍了如何使用单例构建数据模型,如何抽象类、ListView是如何从ArrayAdapter获取数据并呈现视图);

如何创建ArrayAdapter来管理ListView的数据;

如何响应ListView条目的点击事件;

如何定制化ListView条目的布局(默认的布局仅是一个textview);

如何从Fragment中启动并把参数传递给另一个Activity;

如何从Activity传递参数给它托管的Fragment(直接获取Activity extra,通过fragment argument bundle);

如何通知Fragment的Hosting Activity返回结果;

如何在Fragment内获取返回结果;

如何通过ViewPager托管Fragment,以实现屏幕滑动的效果;

如何在同一个Activity托管的两个Fragment之间传递数据;

如何根据屏幕大小选择布局;

贰跟着这些章节顺序完整地实现了这个App,对于理解上述主题,非常有帮助。并且通过GitHub记录了实现的过程:Learning_Android_Criminal_Intent
下面gif展示的内容涉及到Fragment和ViewPager:

这篇笔记主要是整理总结与Fragment相关的部分,整理自上述提到的章节。


Fragment是什么 & Activity如何管理一个Fragment

UI Fragment可以管理界面,整屏或部分。有自己的布局文件,包含了用户可以交互的可视化UI元素。
用UI Fragment将应用的UI分解成块,利用一个个构建块,很容易做到构建分页界面、动画侧边栏界面等更多其他定制界面。
Fragment不具有在屏幕上显示视图的能力。因此,只有将它的视图放置在activity的视图层级结构中(称之为托管Hosting UI Fragment),fragment视图才能显示在屏幕上。

在activity代码中添加fragment, 可以在运行时控制fragment,我们可以决定何时将fragment添加到activity中以及随后可以完成何种具体任务;也可以移除fragment,用其他fragment代替当前fragment,然后再重新添加已移除的fragment。

需要在activity视图层级结构中为fragment视图安排位置,创建fragment容器布局

step1/3 创建Fragment容器布局
activity_crime.xml
step2/3 创建UI Fragment

但此时,activity还未托管fragment,所以代码运行后UI看不到任何内容。接下来需要编写代码,创建UI Fragment,覆写fragment的生命周期函数(几乎对应到activity的声明周期函数)。
创建fragment和创建activity步骤相同:定义布局文件、创建fragment子类、在代码中关联布局文件声明的组件。

public class CrimeFragment extends Fragment {
    private Crime mCrime;
    private EditText mTitleField;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCrime = new Crime();
    }

    @Override
    // 由onCreateView方法生成fragment的视图
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_crime, parent, false);
        // 调用View.findViewById(int)
        mTitleField = (EditText)v.findViewById(R.id.crime_title);
        // 监听器方法设置和activity一样
        mTitleField.addTextChangedListener(new TextWathcer() {
            public void onTextChanged(...) {}
            public void beforeTextChanged(...) {}
            public void afterTextChanged(...) {}
        });

        return v;
    }
}
step3/3 添加UI Fragment到FragmentManager

但此时运行,仍然看不到fragment,还需要将fragment的视图放置到FrameLayout容器中,以添加给activity。所以并没有“start fragment”这个概念
FragmentManager类负责管理fragment并将它们的视图添加到activity的视图层级结构中。fragment transactions(事务)被用来添加、移除、附加、分离或替换fragment队列中的fragment。这是使用fragment在运行时组装和重新组装用户界面的核心方式。FragmentManager管理着fragment transactions的回退栈。

import android.support.v4.app.Fragment
public class CrimeActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime);

        FragmentManager fm = getSupportFragmentManager();

        // 使用R.id.fragmentContainer的容器视图资源ID,向FragmentManager请求获取fragment。如要获取的fragment在队列中已经存在,FragmentManager随即会将之返还。
        Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

        // 如指定容器视图资源ID的fragment不存在,则fragment变量为空值。
        // 这时应创建一个新的CrimeFragment,并创建一个新的fragment transaction用来把新建的fragment添加到队列中。
        if (fragment == null) {
            fragment = new CrimeFragment();
            fm.beginTransaction()
                .add(R.id.fragmentContainer, fragment)
                .commit();
        }
    }
}  

FragmentManager保持fragment与activity的状态一致,但fragment方法究竟是在activity方法之前还是之后调用的这一点是无法保证的。


使用ListFragment显示列表

ListView只有在需要显示某些列表项(list item是listview的一个child view object,可以是简单地 view,可以是复杂的view)时,它才会去申请可用的视图对象;如果为所有的列表项数据创建视图对象,会浪费内存;

ListView找谁去申请视图对象呢? 答案是adapter。adapter是一个控制器对象,负责从模型层获取数据,创建并填充必要的视图对象,将准备好的视图对象返回给ListView;

首先,通过调用adapter的getCount()方法,ListView询问数组列表中包含多少个对象(为避免出现数组越界的错误);紧接着ListView就调用adapter的getView(int, View, ViewGroup)方法。

创建默认的列表项
public class CrimeListFragment extends ListFragment{
    private static final String TAG = "CrimeListFragment";
    private ArrayList mCrimes;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActivity().setTitle(R.string.crimes_title);
        mCrimes = CrimeLab.get(getActivity()).getCrimes();
        
        // android.R.layout.simple_list_item_1)是Android SDK提供的列表项的布局资源,仅包含一个TextView;
        // 默认的ArrayAdapter.getView(...)方法依赖于toString()方法。
        // toString()方法等价于getClass().getName() + "@" + Integer.toHexString(hashCode()) ,返回了混和对象类名和内存地址的字符串信息。
        ArrayAdapter adapter =
                new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, mCrimes);
        setListAdapter(adapter);
    }
    
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        Crime c = (Crime) l.getAdapter().getItem(position);
        Log.d(TAG, c.getTitle() + " was clicked");
    }
}
定制列表项

创建定义列表项视图的XML布局文件,替代默认的列表项布局文件;

创建ArrayAdapter的子类,并override public View getView(int position, View convertView, ViewGroup parent)

Get a View that displays the data at the specified position in the data set. 而默认的getView方法调用的是toString方法;

getView执行时,首先检查是否有复用对象converView,没有则从定制的布局文件中inflate一个新的视图;然后调用Adapter.getItem()方法获取position位置的对象;再然后引用视图对象中的组件,赋予对象的信息;

然后在CrimeListFragment中绑定定制的adapter,更新onCreate(...)和onListItemClick(...)

由于CheckBox默认是focusable的,点击列表项时会被解读为切换checkbox状态,然后就无法触发onListItemClick(),所以出现在列表项布局内的任何可聚焦组件(如CheckBox或Button)都应设置为非聚焦状态,从而保证用户在点击列表项后能够获得预期效果。

 public class CrimeListFragment extends ListFragment{
         
-        ArrayAdapter adapter =
-                new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, mCrimes);
+        CrimeAdapter adapter = new CrimeAdapter(mCrimes);
     
+    private class CrimeAdapter extends ArrayAdapter {
+        public CrimeAdapter(ArrayList crimes) {
+            super(getActivity(), 0, crimes);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            // If we weren"t given a view, inflate one
+            if (convertView == null) {
+                convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_crime, null);
+            }
+            
+            // Configure the view for this crime
+            Crime c = getItem(position);
+            TextView titleTextView = (TextView) convertView.findViewById(R.id.crime_list_item_titleTextView);
+            ……
+            return convertView;
+        }
+    }
 }
重新加载显示列表项

如模型层保存的数据发生改变(或可能发生改变),应通知列表视图的adapter,以便其及时获取最新数据并重新加载显示列表项。在适当的时点,与系统的ActivityManager回退栈协同运作,可以完成列表项的刷新。
CrimeListActivity恢复运行状态后,操作系统会向它发出调用onResume()生命周期方法的指令。CrimeListActivity接到指令后,它的FragmentManager会调用当前被activity托管的fragment的onResume()方法。
一般来说,要保证fragment视图得到刷新,在onResume()方法内更新代码是最安全的选择。(因为可能只是暂停,而不是停止onStart方法不会被调用到)

 public class CrimeListFragment extends ListFragment{
+    @Override
+    public void onResume() {
+        super.onResume();
+        ((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
+    }

Fragment和Activity之间数据传递

典型的应用场景:ActivityA及其托管的FragmentA,ActivityB及其托管的FragmentB。现在需要从FragmentA中启动并传递数据给ActivityB,ActivityB再把数据传递给FragmentB。

从Fragment启动Activity

从fragment中启动activity的实现方式,基本等同于从activity中启动另一activity的实现方式。调用Fragment.startActivity(Intent)方法,该方法在后台会调用对应的Activity方法;

附加extra信息;

fragment如何从托管它的activity获取extra信息?简单的方法是getActivity().getIntent(),但牺牲了fragment的封装性,因为它总是需要由某个具体activity托管着。

weiyiWorkCell:Learning-Android-CriminalIntent weiyi$ git diff a31d00d  9d3ebdb
 public class CrimeListFragment extends ListFragment{
     @Override
     public void onListItemClick(ListView l, View v, int position, long id) {
         Crime c = ((CrimeAdapter) l.getAdapter()).getItem(position);
-        Log.d(TAG, c.getTitle() + " was clicked");
+        // Start Activity
+        Intent i = new Intent(getActivity(), CrimeActivity.class);
+        i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
+        startActivity(i);
     }

diff --git a/src/me/li2/android/criminalintent/CrimeFragment.java b/src/me/li2/android/criminalintent/CrimeFragment.java
 public class CrimeFragment extends Fragment {
+    public static final String EXTRA_CRIME_ID = "me.li2.android.criminalintent.crime_id";
     // Configure the fragment instance.
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mCrime = new Crime();
+        UUID crimeId = (UUID) getActivity().getIntent().getSerializableExtra(EXTRA_CRIME_ID);
+        mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
     }
附加arguments给Fragment,获取arguments

每个fragment实例都可附带一个Bundle对象。该bundle包含有key-value对,我们可以如同附加extra到Activity的intent中那样使用它们。一个key-value对即一个argument.

首先需创建Bundle对象;然后使用Bundle限定类型的“put”方法(类似于Intent的方法),将argument添加到bundle中。

附加argument bundle给fragment,必须在fragment创建后、添加给activity前完成。所以习惯做法是添加名为newInstance()的静态方法给Fragment类。
使用该方法,完成fragment实例及bundle对象的创建,然后将argument放入bundle中,最后再附加给fragment。

托管activity需要fragment实例时,需调用newInstance()方法,而非直接调用其构造方法。

托管activity就应该知道有关托管fragment方法的细节,但fragment则不必知道其托管activity的细节问题。至少在需要保持fragment通用独立性的时候是如此。

weiyiWorkCell:Learning-Android-CriminalIntent weiyi$ git diff 9d3ebdb acc17af
diff --git a/src/me/li2/android/criminalintent/CrimeActivity.java b/src/me/li2/android/criminalintent/CrimeActivity.java
 public class CrimeActivity extends SingleFragmentActivity { 
     @Override
     protected Fragment createFragment() {
-        return new CrimeFragment();
+        UUID crimeId = (UUID) getIntent().getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);
+        return new CrimeFragment().newInstance(crimeId);
     }
 }

 public class CrimeFragment extends Fragment {
+    public static CrimeFragment newInstance(UUID crimeId) {
+        Bundle args = new Bundle();
+        args.putSerializable(EXTRA_CRIME_ID, crimeId);
+        
+        CrimeFragment fragment = new CrimeFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+    
     @Override
     // Configure the fragment instance.
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        UUID crimeId = (UUID) getActivity().getIntent().getSerializableExtra(EXTRA_CRIME_ID);
+        UUID crimeId = (UUID) getArguments().getSerializable(EXTRA_CRIME_ID);
     }
在Fragment内获取返回结果

fragment有自己的Fragment.startActivityForResult(Intent,int)onActivityResult(...),但却不具有setResult(...)方法,但可以先取得托管它的activity,然后再返回数据。

weiyiWorkCell:Learning-Android-CriminalIntent weiyi$ git diff 8d87138 155c5d8
diff --git a/src/me/li2/android/criminalintent/CrimeFragment.java b/src/me/li2/android/criminalintent/CrimeFragment.java
 public class CrimeFragment extends Fragment {
+    public void returnResult() {
+        getActivity().setResult(Activity.RESULT_OK, null);
+    }
 }

diff --git a/src/me/li2/android/criminalintent/CrimeListFragment.java b/src/me/li2/android/criminalintent/CrimeListFragment.java
 public class CrimeListFragment extends ListFragment{
+    private static final int REQUEST_CRIME = 1;

-        startActivity(i);
+        startActivityForResult(i, REQUEST_CRIME);

+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CRIME) {
+            // Handle result
+        }
     }

ViewPager和Fragment

创建CrimePagerActivity类来管理ViewPager;

定义包含ViewPager的视图层级结构:以代码的方式构建ViewPager:创建资源id,创建实例,设置实例的id,设置为activity的内容视图content view。

在CrimePagerActivity类中关联使用ViewPager及其PagerAdapter(包括FragmentStatePagerAdapter和FragmentPagerAdapter),二者间的配合支持实际归结为两个简单方法的使用,即getCount()和getItem(int)。调用getItem(int)方法获取crime数组指定位置的Crime时,它会返回一个已配置的用于显示指定位置crime信息的CrimeFragment。

修改CrimeListFragment.onListItemClick(...)方法,启动CrimePagerActivity,而非CrimeActivity.

ViewPager.setCurrentItem()方法设置当前显示的page;

使用OnPageChangeListener监听ViewPager当前显示页面的状态变化。onPageScrolled(...)方法可告知我们页面将会滑向哪里;onPageScrollStateChanged(...)方法可告知我们当前页面所处的行为状态,如正在被用户滑动、页面滑动入位到完全静止以及页面切换完成后的闲置状态。

public class CrimePagerActivity  extends FragmentActivity {
    private ViewPager mViewPager;
    private ArrayListmCrimes;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewPager = new ViewPager(this);
        mViewPager.setId(R.id.viewPager);
        setContentView(mViewPager);
        
        mCrimes = CrimeLab.get(this).getCrimes();
        
        FragmentManager fm = getSupportFragmentManager();
        mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
            @Override
            public int getCount() {
                return mCrimes.size();
            }
            
            @Override
            // 因为需要返回Fragment(用于构建activity),所以在构建adapter时,还需传入FragmentManager给它的构造方法。
            public Fragment getItem(int pos) {
                Crime crime = mCrimes.get(pos);
                return CrimeFragment.newInstance(crime.getId());
            }
        });
        
        mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageSelected(int pos) {
                Crime crime = mCrimes.get(pos);
                if (crime.getTitle() != null) {
                    setTitle(crime.getTitle());
                }
            }
            
            @Override
            public void onPageScrolled(int pos, float posOffset, int posOffsetPixels) { }
            
            @Override
            public void onPageScrollStateChanged(int state) {}
        });
        
        UUID crimeId = (UUID) getIntent().getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);
        for (int i=0; i

创建并显示日期选择的对话框(把AlertDialog视图封装在DialogFragment实例中)

不使用DialogFragment,也可显示AlertDialog视图,但Android开发原则不推荐这种做法。使用FragmentManager管理对话框,可使用更多配置选项来显示对话框,比如:gravity, match_parent.

要将DialogFragment添加给FragmentManager管理并放置到屏幕上,可调用DialogFragment.show(FragmentManager fm, String tag)方法。

res/layout/dialog_date.xml



        
        
        

src/me/li2/android/criminalintent/DatePickerFragment.java

public class DatePickerFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // 虽然DatePicker可以直接生成视图,但使用布局文件容易修改对话框的显示内容
        // DatePicker v = new DatePicker(getActivity());
        View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_date, null);

        return new AlertDialog.Builder(getActivity())
            .setView(v)
            .setTitle(R.string.date_picker_title)
            .setPositiveButton(android.R.string.ok, null)
            .create();
    }
}

src/me/li2/android/criminalintent/CrimeFragment.java

public static CrimeFragment newInstance(UUID crimeId) {
        mDateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                DatePickerFragment dialog = new DatePickerFragment();
                dialog.show(fm, DIALOG_DATE);
            }
        });
同一个Activity托管的两个Fragment如何发送数据

到现在为止,CrimeActivity已经包含了2个Fragment:CrimeFragment(继承自Fragment)和DatePickerFragment(继承自DialogFragment)。
问题是,当用户点击CrimeFragment的日期按钮时,怎么把日期传递给DatePickerFragment?

类似从activity传递数据到fragment,替代fragment的构造方法,创建和设置fragment argument通常是在一个newInstance()方法中完成。

weiyiWorkCell:Learning-Android-CriminalIntent weiyi$ git diff 62a68ee 0c6fe64
diff --git a/src/me/li2/android/criminalintent/CrimeFragment.java b/src/me/li2/android/criminalintent/CrimeFragment.java
 public class CrimeFragment extends Fragment {
             @Override
             public void onClick(View v) {
                 FragmentManager fm = getActivity().getSupportFragmentManager();
-                DatePickerFragment dialog = new DatePickerFragment();
+                DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
                 dialog.show(fm, DIALOG_DATE);
             }
         });

diff --git a/src/me/li2/android/criminalintent/DatePickerFragment.java b/src/me/li2/android/criminalintent/DatePickerFragment.java
 public class DatePickerFragment extends DialogFragment {
+    public static final String EXTRA_DATE = "me.li2.android.criminalintent.date";
+    
+    private Date mDate;
+    
+    public static DatePickerFragment newInstance(Date date) {
+        Bundle args = new Bundle();
+        args.putSerializable(EXTRA_DATE, date);
+        
+        DatePickerFragment fragment = new DatePickerFragment();
+        fragment.setArguments(args);
+        
+        return fragment;
+    }
+    
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        mDate = (Date) getArguments().getSerializable(EXTRA_DATE);
         ……
         // 虽然DatePicker可以直接生成视图,但使用布局文件容易修改对话框的显示内容
         // DatePicker v = new DatePicker(getActivity());
         View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_date, null);
         return new AlertDialog.Builder(getActivity())
             .setView(v)
             .setTitle(R.string.date_picker_title)
同一个Activity托管的两个Fragment如何回传结果

现在用户选择完了日期,点击确定按钮后,如何把这个日期数据从DatePickerFragment回传到CrimeFragment呢?

Fragment提供来了另外一种绑定方式:为fragment设置数据返回的目标fragment和请求码:调用Fragment.setTargetFragment(Fragment fragment, int requestCode)。
然后通过getTargetFragment().onActivityResult(getTargetRequestCode(), int resultCode, Intent data)方法实现数据的回传。

weiyiWorkCell:Learning-Android-CriminalIntent weiyi$ git diff 0c6fe64 5a572dc
diff --git a/src/me/li2/android/criminalintent/CrimeFragment.java b/src/me/li2/android/criminalintent/CrimeFragment.java
 public class CrimeFragment extends Fragment {
+    private static final int REQUEST_DATE = 0;

         mDateButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 FragmentManager fm = getActivity().getSupportFragmentManager();
                 DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
+                dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
                 dialog.show(fm, DIALOG_DATE);
             }
         });

+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode != Activity.RESULT_OK) {
+            return;
+        }
+        if (requestCode == REQUEST_DATE) {
+            Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+        }
+    }
 }

diff --git a/src/me/li2/android/criminalintent/DatePickerFragment.java b/src/me/li2/android/criminalintent/DatePickerFragment.java
 public class DatePickerFragment extends DialogFragment {     
+    private void sendResult(int resultCode) {
+        if (getTargetFragment() == null) {
+            return;
+        }
+        
+        Intent i = new Intent();
+        i.putExtra(EXTRA_DATE, mDate);
+        getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i);
+    }
+    
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new AlertDialog.Builder(getActivity())
             .setView(v)
             .setTitle(R.string.date_picker_title)
-            .setPositiveButton(android.R.string.ok, null)
+            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    sendResult(Activity.RESULT_OK);
+                }
+            })
             .create();
     }
+
 }

整理自《安卓权威编程指南 Android Programming - The Big Nerd Ranch Guide》第7~12章、16~22章。
你可以从这里获取源码 Learning_Android_Criminal_Intent


版权声明:《如何使用Android UI Fragment开发“列表-详情”界面?》由 WeiYi.Li 在 2015年09月05日写作。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
文章链接:http://li2.me/2015/09/how-to-develop-lis...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12360.html

相关文章

  • 安卓组件控件

    摘要:优点这样做的好处是无需适配,不用担心便宜问题缺点是不同的界面要写不同的基本使用学习记录开车指南之最全实用案例学习整理,很全安卓约束控件扁平化布局入门好棒的干货一个强悍而优美的视频播放器最近项目中有需要用在线视频播放。 ExpandableListView一点点 学习ExpandableListView简单用法 FloatingDragButton:炫酷的拖拽浮动按钮 IOS的Assis...

    tainzhi 评论0 收藏0
  • Android基础:Fragment,看这篇就够了

    摘要:也有类似的栈,称为回退栈,回退栈是由管理的。为的参数,通过能找到回退栈的特定元素,可以为或者,表示只弹出该元素以上的所有元素,表示弹出包含该元素及以上的所有元素。是异步执行的,是丢到主线程的执行,是同步版本。 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 由 天天P图攻城狮 发布在云+社区 作者简介:damonxia(夏正冬),天天P图Android工程师 下文中Demo的源...

    littleGrow 评论0 收藏0
  • 练习demo - 收藏集 - 掘金

    摘要:同时我们或许还会有这样的体验打开联系人界面,手指向上滑动,联系人列表也会跟着一动态权限申请步骤以及需要注意的一些坑掘金因为工作需要,简单研究了一下权限申请,在提供的的基础上,写了一个简单的。 自定义未读消息红点提示 - Android - 掘金未读消息提示,可自定义颜色及Padding 效果展示... Android 实现底部对话框 - Android - 掘金最近项目上需要实现一个底...

    jerryloveemily 评论0 收藏0
  • 分类整理我在SF上针对某些问题作的回答

    摘要:是什么意思是类型,意图是通过数字获取对应的类如果,返回的就是如果,返回的就是当拿到类后,通过调用该类的无参数构造器,创建并返回该类的一个实例,等价于。 Android 资源Resource与布局Layout android:怎么实现一个控件与另一个指定控件左对齐 针对你这种情况,最简单的一种办法是,设置两个TextView的宽度为固定值,且相等。LinearLayout是一种线性排列的...

    paulquei 评论0 收藏0
  • Android控件 - 收藏集 - 掘金

    摘要:贝塞尔曲线根据万能的,支持上拉加载添加掘金最近的项目,使用比较多,导致需要写大量的和。转载请注明来源会了这些,你也能成为自定义大咖掘金自定义自定义主要掌握以下四块内容绘制机制掌握及相关类的使用。 ViewPager+Fragment 组合的预加载和懒加载 - Android - 掘金转载请标明出处: http://www.jianshu.com/p/7a47... 预加载介绍 View...

    ACb0y 评论0 收藏0

发表评论

0条评论

ormsf

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<