如何使用RecyclerView

来源:互联网 发布:linux 修改主机名 编辑:程序博客网 时间:2024/06/08 01:36

1. Overview

 

    这是一篇翻译文章,自己作为android工程师以来,第一次翻译国外网站的文章,所以不免会有一些遗漏和错误。

    原文请参考

    RecyclerView 是一种新的ViewGroup,它的存在是为了像其他的viewGroup一样提供以adapter为基础的view。它将成为ListViewGridView的继承者(这也是我翻译这篇文章的原因)。这个类是由support-v7提供的支持;之所以这么说的原因有:recyclerView拥有更加可以拓展的framework,尤其是在它支持了实现水平和垂直的layout。当你的数据集会根据用户的action或者网络事件而进行change的时候要使用RecyclerView

如果你想要使用RecyclerView,你需要进行下面的工作:

  1. . RecyclerView.Adapter-用于处理数据集和绑定它到一个view
  2. .LayoutManager- 用于定位items
  3. .ItemAnimator- 为通用的操作(如:增加和移除item)进行动画处理

另外,它为listView添加和删除添加的动画效果和现在目前的实现是完全不同的。RecyclerView也在开始加强ViewHolderPattern

这个动作目前是一个值得推荐的经验,而且现在已经深深的在新的framework中集成了。

更多的细节请看这个link

1.1 与ListView 对比

RecyclerView 不同于它的父本ListView是因为一下的功能:

  • . Required VIewHolder in Adapters- ListView adapters不要求使用ViewHolder模式去增加性能(一般开发者会用到viewHolder),相反,RecylcerView是被要求使用ViewHolder的。
  • .Customizable Item Layouts ListView 能够且只能够布局垂直Linear而且这个不能由用户定制。相反,RecyclerView因为拥有RecylerView.LayoutManger所以可以允许所有的itemlayouts包括水平liststaggeredgrids
  • . easy item animations listView 并不包含特别提供的产品来提供对于item增减时可以产生动画的内容。相反,RecyclerView拥有RecyclerView.ItemAnimator类来处理itemanimations
  • .Manual Data Source Listview拥有类似与ArrayAdapterCursorAdapter等不同的Adapters用于处理arraysdatabase结果集。相反,RecyclerView.Adapter要求用户实现来支持数据adapter
  • .Manual Item Decoration ListView 拥有androiddivider属性来快速的间隔list中的各item。相对的,RecyclerView要求使用RecyclerViewItemDecoration对象来实现更多的手动的divider修饰。
  • . Manual Click Detection Listview 拥有一个AdapterView.OnItemClickListener接口来绑定按键事件。相应的,REcyclerVIew只支持RecyclerView.OnItemTouchListener,它将管理分散的touch事件,但是RecyclerView却没有创建好了的clickhanding

2. RecyclerView 的组成

2.1 LayoutManagers

一个RecyclerView需要拥有一个layout Manager 和一个Adapter来进行实例化。一个layoutmanager 将itemviews插入到RecyclerView同时决定什么时候重用那些相当用户不在可见的itemviews。
RecyclerView 提供下面这些build-inlayout managers:
  •   LinearLayoutManager 显示在垂直和水平滚动的items
  •   GridLayoutManger 显示在grid中的items
  •   StaggerdGridLayoutManager 显示在staggered grid 中的items
在创建一个用户的layout manager,extend RecyclerView.LyaoutManager类.
这个link 里面是 Dave Smith’s talk 关于custom layout manager
 

2.2 RecyclerVIew.Adapter

RecyclerView 包含一个新品质的adapter。它类似于一个你已经使用过的adpter,但又具备了一些特殊的特征,例如,要求ViewHolder.你将必须override两个主要的方法:一个是inflate这个view和它的viewholder,另一个是bind数据给这个view. 好消息是第一个方法只是在我们真的需要创建一个新view的时候。不需要检查它是否被回收了。

 

2.3 ItemAnimator

RecyclerView.ItemAnimator将使ViewGroup给adapter提醒的修改(如:增删选择)产生动画效果。DefaultItemAnimator将被用来作为基本的默认动画,而且有非常好的作用效果。可以看这个后面相关的section来获取更多的信息
 

3. Using the RecyclerView

使用一个RecyclerView有以下几个关键的步骤:

1.            增加RecyclerView支持库到gradle build file

2.            定义一个modelclass作为datasource

3.            添加一个RecyclerView给你的activity来显示这个items

4.            创建一个customrow layout XML文件来可视化一个item

5.            创建一个RecyclerView.AdapterViewHolder来提供给item

6.            绑定这个adapterdatasourcepopulatethe RecyclerView.

这些步骤在下面会有详细的阐述

3.1 Installation

确认recyclerView支持库在你的app/build.gradle中列出了了:

 

dependencies {    ...    compile 'com.android.support:recyclerview-v7:23.2.1'}

3.2 定义一个Model

每个RecyclerView 都是有一个数据集的作为背景的。由此,我们将定义一个Contact 类,这个类将代表需要被RecylcerView使用的数据模型。

public class Contact {    private String mName;    private boolean mOnline;    public Contact(String name, boolean online) {        mName = name;        mOnline = online;    }    public String getName() {        return mName;    }    public boolean isOnline() {        return mOnline;    }    private static int lastContactId = 0;    public static ArrayList<Contact> createContactsList(int numContacts) {        ArrayList<Contact> contacts = new ArrayList<Contact>();        for (int i = 1; i <= numContacts; i++) {            contacts.add(new Contact("Person " + ++lastContactId, i <= numContacts / 2));        }        return contacts;    }}

3.3 创建一个RecycleerView 的layout

activitylayout XML文件中,添加一个RecyclerView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >        <android.support.v7.widget.RecyclerView      android:id="@+id/rvContacts"      android:layout_width="match_parent"      android:layout_height="match_parent" /></RelativeLayout>
<p><span style="font-size:14px;">在<span style="font-family:Calibri;">layout</span>的<span style="font-family:Calibri;">preview</span>窗口我们将看到在<span style="font-family:Calibri;">activity</span>中的<span style="font-family:Calibri;">RecylcerView</span>。</span></p>

3.4 创建Custom Row Layout

我们在创建adapter之前,我们需要先创建在每一row中需要使用的XMLlayout 文件。现在我们试图穿件一个水平的Linearlayout,它带有一个textVIew用于显示名字,和一个button,来提醒这个person:
 

这个layout文件可以在res/layout/item_contact.xml创建,而且它将提供给每行item。注意你应该为属性layout_height使用wrap_content,因为在先于版本23.2.1RecyclerView忽略了layout参数。可以参考这个link for 更多的信息。

<?xml version="1.0" encoding="utf-8"?><LinearLayout        xmlns:android="http://schemas.android.com/apk/res/android"        android:orientation="horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:paddingTop="10dp"        android:paddingBottom="10dp"        >    <TextView        android:id="@+id/contact_name"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        />    <Button        android:id="@+id/message_button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:paddingLeft="16dp"        android:paddingRight="16dp"        android:textSize="10sp"        /></LinearLayout>
在定义了custom item layout之后,我们可以创建一个adapter来放置数据到这个recyclerview中。

3.5 Creating the RecyclerView.Adapter

我们将在这里创建一个adapter,这个将从实际上将数据填充到RecyclerView中。这个Adapter的角色是将一个position上的对象转变从list上面的一个item并插入之。

然而,这个adapter要求存在一个“ViewHolder”对象,这个对象描述和提供进入每一行的所有的views。我们可以在ConatactsAdapter.java中提供基本的空adapter holder

 

// Create the basic adapter extending from RecyclerView.Adapter// Note that we specify the custom ViewHolder which gives us access to our viewspublic class ContactsAdapter extends     RecyclerView.Adapter<ContactsAdapter.ViewHolder> {    // Provide a direct reference to each of the views within a data item    // Used to cache the views within the item layout for fast access    public static class ViewHolder extends RecyclerView.ViewHolder {        // Your holder should contain a member variable        // for any view that will be set as you render a row        public TextView nameTextView;        public Button messageButton;        // We also create a constructor that accepts the entire item row        // and does the view lookups to find each subview        public ViewHolder(View itemView) {            // Stores the itemView in a public final member variable that can be used            // to access the context from any ViewHolder instance.            super(itemView);            nameTextView = (TextView) itemView.findViewById(R.id.contact_name);            messageButton = (Button) itemView.findViewById(R.id.message_button);        }} }现在我们已经定义了基本的adapter和ViewHolder,我们现在需要填充adapter。首先,让我们保存一个变量用于contacts list,同时通过构造器传递这个listpublic class ContactsAdapter extends     RecyclerView.Adapter<ContactsAdapter.ViewHolder> {    // ... view holder defined above...    // Store a member variable for the contacts    private List<Contact> mContacts;    // Store the context for easy access    private Context mContext;    // Pass in the contact array into the constructor    public ContactsAdapter(Context context, List<Contact> contacts) {        mContacts = contacts;        mContext = context;    }    // Easy access to the context object in the recyclerview    private Context getContext() {       return mContext;    }}

每个adapter拥有3个基本方法:onCreateViewHolder用来inflate一个itemlayout和创建这个holderonBindViewHolder用于设置基于数据的view的属性,getItemCount来获取items的数量。我们需要implement所有的这三个来完成adapter

public class ContactsAdapter extends     RecyclerView.Adapter<ContactsAdapter.ViewHolder> {    // ... constructor and member variables    // Usually involves inflating a layout from XML and returning the holder    @Override    public ContactsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        Context context = parent.getContext();        LayoutInflater inflater = LayoutInflater.from(context);        // Inflate the custom layout        View contactView = inflater.inflate(R.layout.item_contact, parent, false);        // Return a new holder instance        ViewHolder viewHolder = new ViewHolder(contactView);        return viewHolder;    }        // Involves populating data into the item through holder    @Override    public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {        // Get the data model based on position        Contact contact = mContacts.get(position);        // Set item views based on your views and data model        TextView textView = viewHolder.nameTextView;        textView.setText(contact.getName());        Button button = viewHolder.messageButton;        button.setText("Message");    }    // Returns the total count of items in the list    @Override    public int getItemCount() {        return mContacts.size();    }}

adapter完成后,所有剩余的工作是绑定数据从adapterRecyclerView

3.6 Binding the Adapter to the RecyclerView

在我们的activyt中,我们创建了一个用户集用于在recyclerview中显示。


 

public class UserListActivity extends AppCompatActivity {     ArrayList<Contact> contacts;     @Override     protected void onCreate(Bundle savedInstanceState) {         // ...         // Lookup the recyclerview in activity layout         RecyclerView rvContacts = (RecyclerView) findViewById(R.id.rvContacts);         // Initialize contacts         contacts = Contact.createContactsList(20);         // Create adapter passing in the sample user data         ContactsAdapter adapter = new ContactsAdapter(this, contacts);         // Attach the adapter to the recyclerview to populate items         rvContacts.setAdapter(adapter);         // Set layout manager to position the items         rvContacts.setLayoutManager(new LinearLayoutManager(this));         // That's all!     }}

最后,编译运行,将看到下面图像类似的结果。如果你创建了足够多的items,当scroll这个list,这个views将循环利用而且相对listView更加顺畅的滚动。


3.7 Notifying the Adapter

不像ListViewRecyclerView没有直接添加或者删除item的方法。你需要直接修改原始数据并且通知Adapter相关的任何修改。同时,在你添加或者删除元素的时候,需要同时修改已经存在的list。例如,重新初始化下面的contactslist将不会产生任何对adapter的效果,因为它拥有一个对oldlistmemory的一个引用。

// do not reinitialize an existing reference used by an adapterContacts = Contact.createContactsList(5);

Instead,你需要直接对已经存在的引用进行操作:

// add to the existing listContacts.addAll(Contact.createContactsList(5));
有很多接口用于提醒adapter关于不同的修改:

 

MethodDescription

notifyItemChanged(int pos)      

Notifythat item at position has changed.notifyItemInserted(intpos)Notifythat item reflected at position has been newly inserted.notifyItemRemoved(intpos)

Notify that items previously located at position has beenremoved from the data set.

notifyDataSetChanged()                             

Notify that the dataset has changed. Use only as lastresort.


我们能够如下在activityfragment中使用它们

// Add a new contactcontacts.add(0, new Contact("Barney", true));// Notify the adapter that an item was inserted at position 0Adapter.nofityItemInserted(0);

每当我们想从RecyclerView添加或者删除items的时候,我们需要清楚的通知adapter这个事件。不像ListViewadapter,一个RecyclerViewadapter不能够依赖notifyDataSetChanged(),而是需要使用更加细粒度的action。可以参考如下API文档。

另外,如果你试图update一个存在的list,在进行任何修改前,确定你获取到了当前item的数量。例如,在adapter上面的getItemCount()应该被调用来记录即将修改的第一个index

// record this value before making any changes to the existing listint curSize = adapter.getItemCount();// replace this line with wherever you get new recordsArrayList<Contact> newItems = Contact.createContactsList(20); // update the existing listcontacts.addAll(newItems);// curSize should represent the first element that got added// newItems.size() represents the itemCountAdapter.nofityItemRangeInserted(curSize,newItems,size());

3.8 滚动到新的items

如果我们给要在list的前面插入元素而且想让这个元素保持在最前面,我们可以将位置滚动到1st 元素
adapter.notifyItemInserted(0);  rvContacts.scrollToPosition(0);   // index 0 position

如果我们要在list的最后添加items而且想要滚动到底部作为item的添加。我们可以提醒这个adapter一个添加的元素已经被添加,而且能够调用RecyclerViewsmoothScrollToPosition()

adapter.notifyItemInserted(contacts.size() - 1); // contacts.size() - 1 is the last element positionrvContacts.scrollToPosition(mAdapter.getItemCount() - 1); // update based on adapter

3.9 Implementing Endless Scrolling

 
为了实现集成更多的数据,并且像用户滚动到list的底部一样滚动到底部,可以使用RecyclerView中的addOnScrollListener()并且添加一个onLoadMore方法leveraging theEndlessScrollViewScrollListener文档。

4. Configuring the RecyclerView

RecyclerVIew 是一个非常的具有弹性和用户定制化的控件。一些可用的options可以在下面看到。

4.1 Performance

在所有的item是同样的高度和宽度的情况下,我们可以使得滚动更加的平滑:   

recyclerView.setHasFixedSize(true);

4.2 Layouts

每个item的位置是被layout manager来进行配置的。默认情况下,我们可以选择LinearLayoutManagerGridLayoutManager,和StaggerdGridLayoutManagerLinear显示items要么是水平的要么就是垂直的:

 

// Setup layout manager for itemsLinearLayoutManager layoutManager = new LinearLayoutManager(this);// Control orientation of the items// also supports LinearLayoutManager.HORIZONTALlayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// Optionally customize the position you want to default scroll tolayoutManager.scrollToPosition(0);// Attach layout manager to the RecyclerViewrecyclerView.setLayoutManager(layoutManager);

使用grid 或者 staggerd grid(交错的grid)也可以有类似的操作:

// First param is number of columns and second param is orientation i.e Vertical or HorizontalStaggeredGridLayoutManager gridLayoutManager =     new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);// Attach the layout manager to the recycler viewrecyclerView.setLayoutManager(gridLayoutManager);

例如,一个staggerdgrid 将有如下的显示:

我们可以创建用户的layout mangers,就像这个link提供的一样。

4.3 Decorations

我们可以使用多种装饰品来为recyclerview装饰这些item就像DividerItemDecoration一样:

RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);recyclerView.addItemDecoration(itemDecoration);

这个decorator显示这个分界线的效果图如下:

4.3.2 Grid Spacing Decorations

装饰可以同时被用来添加在grid layoutstaggerd griditems周围的相关空间。可以CopySpacesItemDecoration.java decorator 到你的project同时使用addItemDecoration方法将其应用到RecyclerView。参考staggered grid tutorial 获取更多的细节。

4.4 Animators

RecyclerView支持用户动画来为item提供entermove,或者删除,是通过ItemAnimator来实现这一点的。默认的动画效果是被DefaultItemAnimator 定义的,它复杂的实现方式(看 source code)显示了它的动画效果可以在特殊的操作中(remove moveadd)中同样可以表现的非常好。

目前,最快的实现RecyclerView的动画效果的是使用第3方的lib。这个Third-partyrecyclerview-animators libray包含了很多动画以提供你使用而无需创建你自己的。只需要简单的修改你的app/build.gradle:

repositories {    jcenter()}dependencies {compile 'jp.wasabeef:recyclerview-animators:2.2.0'}

接着,我们就可以使用任何已经定义的动画来修改RecyclerView的行为了:

recyclerView.setItemAnimator(new SlideInUpAnimator());
<img src="https://i.imgur.com/v0VyQS8.gif" alt="" />

4.4.2 New ItemAnimator interface

从为了支持RecyclerViewsupportv23.1.0 library开始,同时也为ItemAnimator接口提供了一个新的接口。这个老的接口已经被deprecated to SimpleItemAnimator。这个lib 添加了一个ItemHolderInfo 类,他的表现和被DefaultItemAnimator定义MoveInfo类相似,但是被使用的更加普遍以在animationtransition states中传递状态信息。很可能在下一个版本的defaultItemAnimator中将简化的使用这个新的类和新定制的接口。

4.5 Heterogeneous Views

如果你想inflate多种类型的row到一个单独的RecyclerVIew请看link中的guide这个将可以帮助我们将包含多种不同类型的items添加到一个single list,效果图如下:

在single list中包含非常多不同类型的items的时候,这个设计时非常有用的。

4.6 Handling Touch Events

RecyclerView 允许我们处理touch events使用下面的代码:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {    @Override    public void onTouchEvent(RecyclerView recycler, MotionEvent event) {        // Handle on touch events here    }    @Override    public boolean onInterceptTouchEvent(RecyclerView recycler, MotionEvent event) {        return false;    }});

4.7 Attaching Click Handlers to Items

4.7.1 Attaching Click Listeners with Decorators

最早的解决RecyclerView的处理item click handlers 的解决方案是使用一个decorator类来管理itemclick listener。在拥有了这个Clever ItemClickSupportdecorator后,添加一个clickhandler可以用下面的方式:

ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(  new ItemClickSupport.OnItemClickListener() {      @Override      public void onItemClicked(RecyclerView recyclerView, int position, View v) {          // do it      }} );
在掩护下,这个接口的封装会在下面有详细的描述。如果你使用了上面的这些code,你你讲不需要下面的任何的手动clickhanding。这个技术最原始的来历是在这个link中。

4.7.2 Simple Click Handler within ViewHolder

RecyclerView 不像ListView拥有setOnItemClickListener一样拥有一些特殊的前置的给itemsattach click handlers的方法。为了实现同样的效果(代替上面的使用decorator utility),我们可以将clickevent attach到我们的adapter中的ViewHolder中:

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> {    // ...    // Used to cache the views within the item layout for fast access    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {        public TextView tvName;        public TextView tvHometown;        private Context context;        public ViewHolder(Context context, View itemView) {            super(itemView);            this.tvName = (TextView) itemView.findViewById(R.id.tvName);            this.tvHometown = (TextView) itemView.findViewById(R.id.tvHometown);            // Store the context            this.context = context;            // Attach a click listener to the entire row view            itemView.setOnClickListener(this);        }        // Handles the row being being clicked        @Override        public void onClick(View view) {            int position = getLayoutPosition(); // gets item position            User user = users.get(position);            // We can access the data within the views            Toast.makeText(context, tvName.getText(), Toast.LENGTH_SHORT).show();        }    }// ...}

如果我们想在press item时显示“selected”效果,我们可以给rowrootlayout设置androidbackground属性成为?android:attr/selectableItemBackground:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal" android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="?android:attr/selectableItemBackground"> <!-- ... --></LinearLayout>


这个将带来如下图所示的效果:

4.7.3 Attaching Click Handlers using Listeners

在实际的运用中,你可能在RecyclerView中想给view设置clickhandlers,但是将click的逻辑定义在Activity或者Fragment中。为了达到这个目的,在adapter中创建一个 custom listener(http://guides.codepath.com/android/Creating-Custom-Listeners)然后firethe events upwards to an interface implementation defined within the parent :
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> {    // ...    /***** Creating OnItemClickListener *****/        // Define listener member variable        private static OnItemClickListener listener;    // Define the listener interface    public interface OnItemClickListener {        void onItemClick(View itemView, int position);    }    // Define the method that allows the parent activity or fragment to define the listener    public void setOnItemClickListener(OnItemClickListener listener) {        this.listener = listener;    }    public static class ViewHolder extends RecyclerView.ViewHolder {        public TextView tvName;        public TextView tvHometown;        public ViewHolder(final View itemView) {            super(itemView);            this.tvName = (TextView) itemView.findViewById(R.id.tvName);            this.tvHometown = (TextView) itemView.findViewById(R.id.tvHometown);            // Setup the click listener            itemView.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    // Triggers click upwards to the adapter on click                    if (listener != null)                        listener.onItemClick(itemView, getLayoutPosition());                }            });        }    }    // ...}
然后,我们可以attach a click handler adapter,如下:
// In the activity or fragmentContactsAdapter adapter = ...;adapter.setOnItemClickListener(new ContactsAdapter.OnItemClickListener() {    @Override    public void onItemClick(View view, int position) {        String name = users.get(position).name;        Toast.makeText(UserListActivity.this, name + " was clicked!", Toast.LENGTH_SHORT).show();}});
可以参考这个stackoverflow post来看看如何在RecyclerView中设置一个item-levelclickhandlers

5. Implementing Pull to Refresh

SwipeRefreshLayout 应该在当RecyclerVIew像通过向下拉的动作刷新内容的时候使用到。请参考我们的关于RecyclerView with SwipeRefreshLayout 的详细的detailguide,一步一步的实现下来刷新。


6. References

•https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
•http://www.grokkingandroid.com/first-glance-androids-recyclerview/
•http://www.grokkingandroid.com/statelistdrawables-for-recyclerview-selection/
•http://www.bignerdranch.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/
•https://developer.android.com/training/material/lists-cards.html
•http://antonioleiva.com/recyclerview/
•https://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
•https://code.tutsplus.com/tutorials/introduction-to-the-new-lollipop-activity-transitions--cms-23711


 


 

 
 


 

 


 

 



 

 


 

 

 

 


 


 



 



 

 

 

 




 

 

 

 

 

 

 

 

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新买的鞋子臭怎么办 当公民利益受到侵犯怎么办 唇钉里面长肉怎么办 宝宝舔了一口酒怎么办 头被玻璃门撞了怎么办 30多了还一事无成 未来怎么办 27岁失业了该怎么办 无业证明不给开怎么办 典型的缺乏运动的肥胖怎么办 30岁了不想结婚怎么办 专家解释欠30万怎么办 欠医院十几万钱怎么办 当你迷茫的时候怎么办 被骗了一年的积蓄怎么办 当兵身高视力都不够怎么办 当兵中途不想当了怎么办 当兵后不想当了怎么办 在泰国想剪头发怎么办 省二证书丢了怎么办 职称计算机级别报错怎么办 科一不会用电脑怎么办 西安科目三挂了怎么办 我有c照想考a照怎么办 叉车证单位不给怎么办 刚练科目三害怕怎么办 摩托科目一考不过怎么办 人老了视力不好怎么办 2岁宝宝视力不好怎么办 唱歌音总是唱不准怎么办 考级没地方练琴怎么办 少儿声乐唱不准音该怎么办 学历认证报告编号忘记了怎么办 自考挂科10门了怎么办 高中的会考没过怎么办 毕业证和学位证丢了怎么办 大学毕业证学位证丢了怎么办 大学毕业证和学位证丢了怎么办 毕业证是士官证号码怎么办 自考本科准考证丢了怎么办 自考档案搞丢了怎么办 自考本科档案在自己手里怎么办?