Android开发基础规范(二)
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52614696
前言:Android中一些开发规范,避免给自己和别人少留坑。
二、代码相关
2.2.15 Field Ordering 属性排序 
在类文件顶部声明的任何属性都应该按下列的排序规则进行排序:
- 1.Enums (枚举类型)
 - 2.Constants (常量)
 - 3.Dagger Injected fields (Dagger注入的属性)
 - 4.Butterknife View Bindings (Butterknife绑定的view)
 - 5.private global variables (private成员变量)
 - 6.public global variables      (public成员变量) 
例如: 
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  public static enum {
      ENUM_ONE, ENUM_TWO
  }
  public static final String KEY_NAME = "KEY_NAME";
  public static final int COUNT_USER = 0;
  @Inject SomeAdapter mSomeAdapter;
  @BindView(R.id.text_name) TextView mNameText;
  @BindView(R.id.image_photo) ImageView mPhotoImage;
  private int mUserCount;
  private String mErrorMessage;
  public int mSomeCount;
  public String mSomeString;
使用上述的排序规则有助于保持字段声明的分组,从而增加字段的可读性
2.2.16 Class member ordering 类成员排序
为了提高代码的可读性,组织类成员在一个符合逻辑的方式中是非常的重要,请按下列的排序方式去实现:
- 1.Constants
 - 2.Fields
 - 3.Constructors
 - 4.Override methods and callbacks (public or private)
 - 5.Public methods
 - 6.Private methods
 - 7.Inner classes or interfaces
 
例如:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public class MainActivity extends Activity {
      private int mStepCount;
      public static newInstance() { }
      @Override
      public void onCreate() { }
      public void setColor(Color color) { }
      private int getId() { }
      static class AnInnerClass { }
      interface SomeInterface { }
}
在Android框架中任何生命周期的方法应该在其相应的生命周期中排序,例如:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public class MainActivity extends Activity {
      // Field and constructors
      @Override
      public void onCreate() { }
      @Override
      public void onStart() { }
      @Override
      public void onResume() { }
      @Override
      public void onPause() { }
      @Override
      public void onStop() { }
      @Override
      public void onRestart() { }
      @Override
      public void onDestroy() { }
     // public methods, private methods, inner classes and interfaces
  }
2.2.17 Method parameter ordering 方法的参数排序 
当定义方法时,参数应该按照下列的规则排序:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public Post loadPost(Context context, int postId);
public void loadPost(Context context, int postId, Callback callback);
Context上下文参数应放在第一位,并且Callback回调参数放置在最后
2.2.18 String constants, naming, and values 字符串常量、命名和值 
 当使用字符串常量时,其应该修饰为静态final并且遵循下列规则:
2.2.19 Enums 枚举 
枚举的使用仅仅在实际需要用到时。如果另外一种方法可行,此时应该选择更好的方式去实现它,例如: 
相对于下面这样:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public enum SomeEnum {
    ONE, TWO, THREE
}
更推荐这样做:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  private static final int VALUE_ONE = 1;
  private static final int VALUE_TWO = 2;
  private static final int VALUE_THREE = 3;
2.2.20 Arguments in fragments and activities
在fragment和activity中的参数 
当我们使用Intent或者Bundle传递数据时,值的键必须使用下面定义的约定:
- Activity
- 传递数据到一个activity必须使用一个KEY的引用,像下面这样定义: 
private static final String KEY_NAME = “com.package.name.activity.KEY_NAME”; 
 - 传递数据到一个activity必须使用一个KEY的引用,像下面这样定义: 
 - Fragment 
- 传递数据到一个fragment必须使用一个EXTRA的引用,像下面这样定义: 
private static final String EXTRA_NAME = “EXTRA_NAME”; 
 - 传递数据到一个fragment必须使用一个EXTRA的引用,像下面这样定义: 
 
当创建fragment或者activity的新实例涉及到传递数据时,我们应该提供一个静态的方法来获取新的实例,传递的数据作为参数。例如:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
//Activity中
public static Intent getStartIntent(Context context, Post post) {
      Intent intent = new Intent(context, CurrentActivity.class);
      intent.putParcelableExtra(EXTRA_POST, post);
      return intent;
}
//Fragment中
public static PostFragment newInstance(Post post) {
      PostFragment fragment = new PostFragment();
      Bundle args = new Bundle();
      args.putParcelable(ARGUMENT_POST, post);
      fragment.setArguments(args)
      return fragment;
}
2.2.21 Line Length Limit 行长度限制 
代码的行数字符长度最好不要超过100个字符,这样代码的可读性会更高。有时为了实现上述要求,我们需要做的:
- 1.提取数据到一个局部变量
 - 2.提取代码逻辑到外部的方法
 - 3.将一段较长的代码换行显示
 - 注意:对于代码的注释和导入声明,超过100个字符的限制是可以的。
 
2.2.21.1 Line-wrapping techniques 换行技巧 
当涉及到换行时,有一些情况我们应该保持与格式化代码的一致性。
- 运算符换行 
当我们需要在一个运算公式换行时,需要在运算符前面换行: 
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  int count = countOne + countTwo - countThree + countFour * countFive - countSix
          + countOnANewLineBecauseItsTooLong;
如果需要,你可以直接在“=”后换行:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  int count =
          countOne + countTwo - countThree + countFour * countFive + countSix;
- 方法链 
当涉及到方法链时(这个比较流行,建议写方法链,RxJava几乎都是),每个方法的调用都应该另起一行:
不要下面这样: 
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  Picasso.with(context).load("someUrl").into(imageView);
取而代之应这样:
  Picasso.with(context)
          .load("someUrl")
          .into(imageView);
- 长参数 
对于一个含有长参数的方法,我们在适当的情况下应该换行。例如当声明一个方法时我们应该在最后一个参数的逗号处换行: 
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  private void someMethod(Context context,
                          String someLongStringName,
                          String text,long
                          thisIsALong, String anotherString) {
  }     
当调用这个方法时,我们应该在每个参数的逗号后面换行:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
  someMethod(context,
          "thisIsSomeLongTextItsQuiteLongIsntIt",
          "someText",
          01223892365463456,
          "thisIsSomeLongTextItsQuiteLongIsntIt");
2.2.22 Method spacing(方法间间距) 
在同一个类中,方法与方法之间只需要留有一行的空白,如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public String getUserName() {
    // Code
}
public void setUserName(String name) {
    // Code
}
public boolean isUserSignedIn() {
    // Code
}
2.2.23 Comments(注释)
2.2.23.1 Inline comments(行内注释)
必要的时候,写注释,其他情况下最好不要写注释,从方法名或者成员变量上就能看出做什么。2.2.23.2 JavaDoc Style Comments(java文档的注释风格)
方法的名字应该起的和该方法的功能相对应,有时可以提供JavaDoc风格的注释。方法名起的好会帮助读者更好的理解方法的功能,同时也会让使用者明白传入方法中参数的作用。
/**
 * Authenticates the user against the API given a User id.
 * If successful, this returns a success result
 *
 * @param userId The user id of the user that is to be authenticated.
 */
 public class XXX {
 }
- 2.2.23.3 Class comments(类注释) 
在创建类注释时,它们应该是有意义的,有描述性的,必要的时候使用超链接。如下: 
/**
  * RecyclerView adapter to display a list of {@link Post}.
  * Currently used with {@link PostRecycler} to show the list of Post items.
  */
public class RecyclerView {
}
不要写初创作者信息,因为以后会有很多人在这个类上改来改去,写上作者信息是没有任何意义的。
/**
* Created By yuiop 22/09/2016
*/
public class XXX {
}
2.2.24 Sectioning code(分段代码) 
2.2.24.1 Java code(java代码) 
如果对代码做了“分段”,应该使用下面的方法完成,如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public void method() { }
public void someOtherMethod() { }
/********* MVP Method Implementations  ********/
public void anotherMethod() { }
/********* Helper Methods  ********/
public void someMethod() { }
不能像下面这样:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
public void method() { }
public void someOtherMethod() { }
// Mvp Method Implementations
public void anotherMethod() { }
这样会更容易定位类中方法。
2.2.24.2 Strings file(字符串文件) 
字符串资源文件string.xml中分段注释如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
// User Profile Activity
<string name="button_save">Save</string>
<string name="button_cancel">Cancel</string>
// Settings Activity
<string name="message_instructions">...</string>
这样写不仅可以让string文件看起来整洁,还能在需要更改它们时更容易找到。
2.2.24.3 RxJava chaining(RxJava链接) 
当进行异步操作时,每一步操作都应该在遇到“.”号之前另起一行,如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
return dataManager.getPost()
            .concatMap(new Func1<Post, Observable<? extends Post>>() {
                @Override
                 public Observable<? extends Post> call(Post post) {
                     return mRetrofitService.getPost(post.id);
                 }
            })
            .retry(new Func2<Integer, Throwable, Boolean>() {
                 @Override
                 public Boolean call(Integer numRetries, Throwable throwable) {
                     return throwable instanceof RetrofitError;
                 }
            });
这样会使读者更容易理解接下来的异步操作。 
2.2.25 Butterknife(Butterknife) 
2.2.25.1 Event listeners(事件监听者) 
如有可能,尽量使用ButterKnife绑定监听。举个栗子,可以用ButterKnife替换传统的点击事件:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
mSubmitButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        // Some code here...
    }
  };
换成如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
@OnClick(R.id.button_submit)
public void onSubmitButtonClick() { }
2.3 XML Style Rules(XML文件中样式规则) 
2.3.1 Use self=-closing tags(使用单标记) 
在xml布局中,如果一个viwe没有任何子view,那么就应该使用单标记。 
用这个:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
<ImageView
    android:id="@+id/image_user"
    android:layout_width="90dp"
    android:layout_height="90dp" />
不用这个:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
<ImageView
    android:id="@+id/image_user"
    android:layout_width="90dp"
    android:layout_height="90dp">
</ImageView>
2.3.2 Resource naming(资源命名) 
所有的资源命名规则都应该是小写和下划线的组合,如下: 
2.3.2.1 ID naming(id命名) 
所有的id命名规则都应该用元素作为前缀。
| Element | Prefix | 
|---|---|
| ImageView | image_ | 
| Fragment | fragment_ | 
| RelativeLayout | layout_ | 
| Button | button_ | 
| TextView | text_ | 
| View | view_ | 
例如:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
<TextView
    android:id="@+id/text_username"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
注意:如果一个布局中一种类型的view只有一种,比方toolbar,那么可以直接起名叫toolbar
2.3.2.2 Strings(字符串) 
所有的字符串名字应该以该应用的当前功能页面作为前缀,如下:
| Screen | String | ResourceName | 
|---|---|---|
| Registration Fragment | “Register now” | registration_register_now | 
| Sign Up Activity | “Cancel” | sign_up_cancel | 
| Rate App Dialog | “No thanks” | rate_app_no_thanks | 
如果没法像上面一样命名,咱们可以用下面的方法:
| Prefix | Description | 
|---|---|
| error_ | Used for error messages | 
| title_ | Used for dialog titles | 
| action_ | Used for option menu actions | 
| msg_ | Used for generic message such as in a dialog | 
| label_ | Used for activity labels | 
需要注意以下两点:
- 1、同一个的字符串资源不能在多个文件中共享使用。如果其中的一个页面字符串发生改变也会造成另一个页面的改变从而产生问题。每个页面使用单独的字符串资源会给将来省去很多麻烦。
 - 2、字符串资源必须放在字符串资源文件中,不能写在布局或者类中。
 
2.3.2.3 Styles and themes 
当定义style和theme时,每个单词应该大写开头。如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
AppTheme.DarkBackground.NoActionBar
AppTheme.LightBackground.TransparentStatusBar
ProfileButtonStyle
TitleTextStyle
2.3.3 Attributes ordering(属性排序) 
定义属性不能只为了看起来整洁,同时能够在布局中快速找到属性位置。以下是基本规则:
- 1、viwe的id
 - 2、style
 - 3、布局的宽高
 - 4、其他的布局属性,按照字母顺序排序
 - 5、其他的属性,按照字母顺序排序
 
如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
<Button
    android:id="@id/button_accept"
    style="@style/ButtonStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true"
    android:padding="16dp"
    android:text="@string/button_skip_sign_in"
    android:textColor="@color/bluish_gray" />
注意:在Android studio中快速格式化快捷键是:cmd + shift + L 
这样做,当布局文件发生变化时,可以通过xml属性快速定位。
本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52614696
2.4 Tests style rules(测试风格规则) 
2.4.1 Unit tests(单元测试) 
所有测试类起名字都应该和他们被测试的类相对应,并且以Test作为后缀,如下:
| Class | Test Class | 
|---|---|
| DataManager | DataManagerTest | 
| UserProfilePresenter | UserProfilePresenterTest | 
| PreferencesHelper | PreferencesHelperTest | 
所有的测试方法应该用@Test进行注释,测试方法应该用下面的模板:
@Test
public void methodNamePreconditionExpectedResult() { }
举例,如果我们想测试一个使用不正确邮箱登录的功能,测试方法应该使用如下的:
@Test
public void signUpWithInvalidEmailFails() { }
测试应该将重点放在测试方法赋予的功能名称上面,如果在你的测试方法中还有别的情况需要考虑,这些额外的需要测试的情况应该分到它专门的测试方法中。 
如果一个类中包含许多不同的方法,测试应该在多个测试类中进行拆分-这样有助于测试更易于维护和定位。例如,一个数据库工具类有时候会分解成如下几个测试类:
DatabaseHelperUserTest
DatabaseHelperPostsTest
DatabaseHelperDraftsTest
2.4.2 Espresso tests(功能测试框架Espresso) 
每个Espresso测试类一般都对应一个Activity,所以命名时应该和对应的Activity相一致,其次是测试,如下:
| Class | Test Class | 
|---|---|
| MainActivity | MainActivityTest | 
| ProfileActivity | ProfileActivityTest | 
| DraftsActivity | DraftsActivityTest | 
当使用Espresso API的时候,方法应该换行从而可以让声明更易读,举例如下:
onView(withId(R.id.text_title))
        .perform(scrollTo())
        .check(matches(isDisplayed()))
这种风格的链接调用不仅可以让我们每行不超过100个字符,同时也可以让Espresso测试中的链接更加易读。
Gradle Style(Gradle风格)
3.1 Dependencies(依赖)
3.1.1 Versioning 
如果一个版本号在多个依赖中多被使用,那么应该在依赖的范围内定义成一个变量,如下:
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
final SUPPORT_LIBRARY_VERSION = '23.4.0'
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:percent:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:customtabs:$SUPPORT_LIBRARY_VERSION"
将来如果需要更新依赖,那么只需要更新版本号的变量就可以很轻松的控制所有依赖的版本号。
3.1.2 Grouping(分组)
- 依赖应该以包名来分组,各个组之间应该有一定的间隙,如下:
 
//create by 逆流的鱼yuiop on 2016/9/22
//blog地址:http://blog.csdn.net/hejjunlin
compile "com.android.support:percent:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:customtabs:$SUPPORT_LIBRARY_VERSION"
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.5'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.jakewharton.timber:timber:4.1.2'
compile 'com.github.bumptech.glide:glide:3.7.0'
Compile、testCompile、androidTestCompile依赖同样应该分组到相对应的组别中,如下:
// App Dependencies
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
// Instrumentation test dependencies
androidTestCompile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
// Unit tests dependencies
testCompile 'org.robolectric:robolectric:3.0'
这两种方法都可以很容易的找到特定的依赖关系,需要时,它保证依赖的声明既干净又整洁。
3.1.3 Independent Dependencies(独立的依赖关系)
- 依赖只能应用在应用或者目的测试中,确保使用compile,testCompile,androidTestCompile来编译它们。例如,robolectric依赖只能被用来做单元测试,它应该如下:
 
testCompile 'org.robolectric:robolectric:3.0'
第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。
如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易
Android开发基础规范(二)的更多相关文章
- Android开发基础规范(一)
		
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52602487 前言:Androi ...
 - 20145206实验四《Android开发基础》
		
20145206 实验四<Android开发基础> 实验内容 ·安装Android Studio ·运行安卓AVD模拟器 ·使用安卓运行出虚拟手机并显示HelloWorld以及自己的学号 ...
 - 实验四 Android开发基础
		
实验四 Android开发基础 实验内容 1.安装Android Studio 2.运行安卓AVD模拟器 3.使用安卓运行出虚拟手机并显示HelloWorld以及自己的学号 (一)SDK的安装 (二) ...
 - 20145225《Java程序设计》  实验四 Android开发基础
		
20145225<Java程序设计> 实验四 Android开发基础 实验报告 实验内容 安装Android Studio 运行安卓AVD模拟器 使用安卓运行出虚拟手机并显示HelloWo ...
 - 20145208 实验四 Android开发基础
		
20145208 实验四 Android开发基础 安装Android Studio 安装的具体步骤在老师的链接中已经很详细了,在此就不做赘述了. 在此提出我觉得安装的时候需要注意的两个地方 一是安装地 ...
 - 20145215实验四 Android开发基础
		
20145215实验四 Android开发基础 实验内容 基于Android Studio开发简单的Android应用并部署测试; 了解Android组件.布局管理器的使用: 掌握Android中事件 ...
 - 20165223 实验四 Android开发基础
		
实验四 Android开发基础 目录 一.实验报告封面 二.具体实验内容 (一)Android Stuidio的安装测试 (二)Activity测试 (三)UI测试 (四)布局测试 (五)教材代码测试 ...
 - 20155324 《Java程序设计》实验四 Android开发基础
		
20155324 <Java程序设计>实验四 Android开发基础 实验内容 1.基于Android Studio开发简单的Android应用并部署测试; 2.了解Android.组件. ...
 - 20145221 《Java程序设计》实验报告四:Android开发基础
		
20145221 <Java程序设计>实验报告四:Android开发基础 实验要求 基于Android Studio开发简单的Android应用并部署测试; 了解Android组件.布局管 ...
 
随机推荐
- 机器学习技法:03 Kernel Support Vector Machine
			
Roadmap Kernel Trick Polynomial Kernel Gaussian Kernel Comparison of Kernels Summary
 - Hibernate | Spring JPA | MySQL 使用过程遇到的一些问题
			
1. 使用过程 2. 背景 3. 遇到问题 3.1 不指定Hibernate数据库方言,默认SQL生成方式 3.2 抛出异常Hibernate加入了@Transactional事务不会回滚 3.3 H ...
 - [Codeforces 863A]Quasi-palindrome
			
Description Let quasi-palindromic number be such number that adding some leading zeros (possible non ...
 - [SDOI2010]代码拍卖会
			
题目描述 随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代码库.猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库.iPig不想把代码库给所有想要的小猪,只想给其中的一部分既关 ...
 - 计蒜客NOIP模拟赛(2) D1T2 表演艺术
			
凡和邻家男孩玩完了纸牌,兴致很高,于是准备了一场表演艺术对抗赛. 他特意请来了很多表演艺术家,分成绿黑两队,进行名为 PK,实则捞金的表演. 凡为了捞金,开设了一个赌局,在比赛开始之前招揽人们来押注谁 ...
 - 洛谷P2388 阶乘之乘
			
题目背景 不告诉你-- 题目描述 求出1!*2!*3!*4!*--*n!的末尾有几个零 输入输出格式 输入格式: n(n<=10^8) 输出格式: 有几个零 输入输出样例 输入样例#1: 复制 ...
 - Codeforces Round#402(Div.1)掉分记+题解
			
哎,今天第一次打div1 感觉头脑很不清醒... 看到第一题就蒙了,想了好久,怎么乱dp,倒过来插之类的...突然发现不就是一道sb二分吗.....sb二分看了二十分钟........ 然后第二题看了 ...
 - B/S与C/S架构
			
1.CS.BS架构定义 CS(Client/Server):客户端----服务器结构.C/S结构在技术上很成熟,它的主要特点是交互性强.具有安全的存取模式.网络通信量低.响应速度快.利于处理大量数据. ...
 - Linux学习之CentOS(四)----Linux文件属性、所有者、群组、其他组及文件权限操作简要总结
			
Linux文件属性.所有者.群组.其他组及文件权限操作简要总结 首先介绍一个重要的知识点:文件属性控制权限 [root@www ~]# ls -al total 156 drwxr-x--- 4 ro ...
 - CSS3左右间歇晃动效果
			
今天在做一个活动页面时,产品想要在页面中添加一个吸引人注意的小图片左右晃动的效果,并且该效果是间歇执行的.我一想应该挺简单的吧,二话没说就答应了,谁知在真正实现的时候才发现还是有些许困难的.于是就在网 ...