activity_main.xml

<Button
android:id="@+id/btn01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="AlertDialog" />

<Button
android:id="@+id/btn02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="CustomDialog" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="hahahohohehe"
android:textSize="20dp"/>
<TextView
android:id="@+id/tv01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:textStyle="bold"/>
</LinearLayout>

custom.xml

<ImageButton
android:id="@+id/imgBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/sym_def_app_icon"/>
<EditText
android:id="@+id/editText01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint = "put expected one!!!"/>

MainActivity.java

public class MainActivity extends AppCompatActivity {
Button btn_alert;
Button btn_custom;
TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btn_alert = findViewById(R.id.btn01);
btn_custom = findViewById(R.id.btn02);
textView = findViewById(R.id.tv01);

btn_alert.setOnClickListener(listener);
btn_custom.setOnClickListener(listener);
}
View.OnClickListener listener = new View.OnClickListener(){
public void onClick(View v){
switch (v.getId()){
case R.id.btn01:
new AlertDialog.Builder(MainActivity.this)
.setTitle("aabbccddeefff")
.setMessage("12312312313")
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton("YES", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
Toast.makeText(getBaseContext(), "YES", Toast.LENGTH_LONG).show();
textView.setText("ILikeEliphant");
}
})
.setNegativeButton("NO", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
Toast.makeText(getBaseContext(), "NO", Toast.LENGTH_LONG).show();
textView.setText("choose again");
}
})
.show();
break;
case R.id.btn02:
LinearLayout linearLayout = (LinearLayout)View.inflate(MainActivity.this, R.layout.custom, null);
final EditText editText = linearLayout.findViewById(R.id.editText01);

new AlertDialog.Builder(MainActivity.this)
.setTitle("hehehehehehehe")
.setView(linearLayout)
.setPositiveButton("OK", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
Toast.makeText(getBaseContext(), "OK", Toast.LENGTH_LONG).show();
String winner = editText.getText().toString();
textView.setText(winner);
}
})
.setNegativeButton("CANCEL", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
Toast.makeText(MainActivity.this, "CANCEL", Toast.LENGTH_LONG).show();
}
})
.show();
break;
}
}
};
}

'android studio' 카테고리의 다른 글

activity 추가하기  (0) 2019.08.13
Recycler View  (0) 2019.08.13
alert dialog  (0) 2019.08.13
방향전환 event로 activity 유지하기  (0) 2019.08.13
방향전환시 상태값 저장  (0) 2019.08.12

--alert dialog--

activity_main.xml

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="open alert dialog"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="check"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />

MainActivity.java

public class MainActivity extends AppCompatActivity {
TextView textView;
Button button;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = findViewById(R.id.textView);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
showDialog();
}
});
}
private void showDialog(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("alert");
builder.setMessage("wanna finish?");
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setPositiveButton("yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
String msg = "you have put yes";
textView.setText(msg);
}
});
builder.setNegativeButton("no", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn) {
String msg = "you have put no";
textView.setText(msg);
}
});
builder.setNeutralButton("cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int btn){
String msg = "you have put cancel";
textView.setText(msg);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
}

--progress bar--

activity_main.xml

<ProgressBar
android:id = "@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/btn1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text = "check message"
android:layout_weight="1"/>
<Button
android:id="@+id/btn2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="close"
android:layout_weight="1"/>
</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {
ProgressDialog dialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ProgressBar progressBar = findViewById(R.id.progressBar);
progressBar.setProgress(40);

Button button = findViewById(R.id.btn1);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
dialog = new ProgressDialog(MainActivity.this);
dialog.setMessage("checking the progress");
dialog.show();
}
});
Button button2 = findViewById(R.id.btn2);
button2.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
if(dialog != null){
dialog.dismiss();
}
}
});
}
}

'android studio' 카테고리의 다른 글

Recycler View  (0) 2019.08.13
custom dialog  (0) 2019.08.13
방향전환 event로 activity 유지하기  (0) 2019.08.13
방향전환시 상태값 저장  (0) 2019.08.12
xml을 이용한 focus event  (0) 2019.08.12

Manifest에 configChanges 속성을 설정한후

MainActivity에서 onConfigurationChanged() method를 overriding해준다

//manifests안에 AndroidManifest.xml파일에서 activity tag안에서 configChanges속석을 설정한다

AndroidManifest.xml

<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden">
<!--android:screenOrientation = "landscape"
가로상태로 고정되어 변하지 않는다-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

activity_main.xml

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="try to change the view direction"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

MainActivity.java

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onConfigurationChanged(Configuration configuration){
super.onConfigurationChanged(configuration);

if(configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
displayToast("landscape");
}else if(configuration.orientation == Configuration.ORIENTATION_PORTRAIT){
displayToast("portrait");
}
}
public void displayToast(String message){
Toast.makeText(context: this, message, Toast.LENGTH_LONG).show();
}
}

'android studio' 카테고리의 다른 글

custom dialog  (0) 2019.08.13
alert dialog  (0) 2019.08.13
방향전환시 상태값 저장  (0) 2019.08.12
xml을 이용한 focus event  (0) 2019.08.12
touch event, gesture event  (0) 2019.08.12

res/new/android resource directory를 이용해

layout-land를 생성후 그 폴더안에 activity_main.xml을 생성

//가로방향으로 전환될때 사용하는 폴더

//onSaveInstanceState()를 호출하면서 전황시 상태값이 저장된다

activity_main.xml

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="vertical"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="41dp"
android:layout_marginBottom="29dp"
android:ems="10"
android:hint="text"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="166dp"
android:layout_marginLeft="166dp"
android:layout_marginTop="29dp"
android:layout_marginEnd="157dp"
android:layout_marginRight="157dp"
android:text="check"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />

activity_main.xml

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="horizontal"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="text"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="242dp" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="check"
tools:layout_editor_absoluteX="344dp"
tools:layout_editor_absoluteY="313dp"/>

MainActivity.java

public class MainActivity extends AppCompatActivity {
String inputText;
EditText editText;
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
displayToast("onCreate() call");

//상태값 저장
editText = findViewById(R.id.editText);
button = findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
inputText= editText.getText().toString();
Toast.makeText(getApplicationContext(), "saved"+inputText, Toast.LENGTH_LONG).show();
} //onClick method를 overriding해준다
});
if(savedInstanceState != null){
inputText = savedInstanceState.getString("inputText");

Toast.makeText(getApplicationContext(), "saved value:" +inputText, Toast.LENGTH_LONG).show();
}
}

protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);

outState.putString("inputText", inputText);
}

// protected void onStart(){
// super.onStart();
// displayToast("onStart() call");
// }
protected void onStop(){
super.onStop();
displayToast("onStart() call");
}
protected void onDestroy(){
super.onDestroy();
displayToast("onDestroy() call");
}

public void displayToast(String message){
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}

'android studio' 카테고리의 다른 글

alert dialog  (0) 2019.08.13
방향전환 event로 activity 유지하기  (0) 2019.08.13
xml을 이용한 focus event  (0) 2019.08.12
touch event, gesture event  (0) 2019.08.12
카드뷰(CardView)  (0) 2019.08.12

activity_main.xml

<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="focus event"
android:textSize="20dp"
android:background="#ff0"
android:gravity="center"
android:padding = "10dp"/>
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="put id"
android:padding="10dp"
android:background="@drawable/focus_selector"
android:textColorHint = "#ff0"
android:textSize="20dp"/>
<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="put password"
android:padding="10dp"
android:background="@drawable/focus_selector"
android:textColorHint = "#ff0"
android:textSize="20dp"/>

focus_selector.xml

//res\drawable

<item android:state_focused = "true"
android:drawable="@drawable/red"/>
<item android:drawable="@drawable/blue"/>

MainActivity.java

'android studio' 카테고리의 다른 글

방향전환 event로 activity 유지하기  (0) 2019.08.13
방향전환시 상태값 저장  (0) 2019.08.12
touch event, gesture event  (0) 2019.08.12
카드뷰(CardView)  (0) 2019.08.12
버튼 style변경(onDraw(), invalidate())  (0) 2019.08.12

activity_main.xml

<View
android:id="@+id/view1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_orange_dark"/>
<View
android:id="@+id/view2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_blue_light"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>

MainActivity.java

public class MainActivity extends AppCompatActivity {
TextView textView;

GestureDetector gestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = findViewById(R.id.textView);

View view = findViewById(R.id.view1);

view.setOnTouchListener(new View.OnTouchListener(){
public boolean onTouch(View view, MotionEvent mE){
int action = mE.getAction();//어떤 action인지 알기위해
float x = mE.getX();
float y = mE.getY();

if(action == MotionEvent.ACTION_DOWN){
output("touched"+ x +", "+y+"\n");
//append는 계속해서 내용을 추가한다
}else if(action == MotionEvent.ACTION_MOVE){
output("moved"+ x +", "+y+"\n");
}else if(action == MotionEvent.ACTION_UP){
output("gone"+ x +", "+y+"\n");
}
return true;
}
});

//getsture event
View view2 = findViewById(R.id.view2);
view2.setOnTouchListener(new View.OnTouchListener(){
public boolean onTouch(View view, MotionEvent mE){
gestureDetector.onTouchEvent(mE);
return true;
}
});
gestureDetector = new GestureDetector(this, new GestureDetector.OnGestureListener(){
public boolean onDown(MotionEvent mE){
output("onDown() call");
return true;
}
//화면에 터치했다가 떨어질때 발생하는 event
public void onShowPress(MotionEvent mE){
output("onShowPress() call");
}
//한손으로 터치했다 떨어지는 event 처리
public boolean onSingleTapUp(MotionEvent mE){
output("onSingleTapUp() call");
return true;
}
//손가락으로 오랫동안 터치하는 event 처리
public void onLongPress(MotionEvent mE){
output("onLongPress() call");
}
//터치된 채 일정한 속도와 방향으로 움직였다 떨어지는 event
public boolean onScroll(MotionEvent mE, MotionEvent mE2, float distanceX, float distanceY){
output("onScroll() call"+distanceX+", "+distanceY);
return true;
}
//터치된채 가속도를 내서 움직였다 떨어지는 event처리
public boolean onFling(MotionEvent mE, MotionEvent mE2, float vX, float vY){
output("onFling() call"+vX+", "+vY);
return true;
}
});
}
public void output(String data){
textView.append(data+"\n");
}
}

'android studio' 카테고리의 다른 글

방향전환시 상태값 저장  (0) 2019.08.12
xml을 이용한 focus event  (0) 2019.08.12
카드뷰(CardView)  (0) 2019.08.12
버튼 style변경(onDraw(), invalidate())  (0) 2019.08.12
toast message, style  (0) 2019.08.11

activity_main.xml

<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="icon change"/>
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="icon change"/>

<com.example.myapplication.Layout01
android:id="@+id/layout01"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

layout.xml

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor = "#eeeeee"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true">

<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/imageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:padding="10dp"
app:srcCompat="@mipmap/ic_launcher"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="title"
android:textSize = "26sp"/>
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="contents"
android:textColor="#00f"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

Layout01.java

public class Layout01 extends LinearLayout {
ImageView imageView;
TextView textView;
TextView textView2;

public Layout01(Context context){
super(context);
init(context);
//생성자가 생성될때 inflation이 같이 되도록 하기위해
}//생성자
public Layout01(Context context, AttributeSet attrs){
super(context, attrs);
init(context);
}//xml속성을 활용하기위해

private void init(Context context){
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.layout, root: this, attachToRoot: true);
imageView = findViewById(R.id.imageView);
textView = findViewById(R.id.textView);
textView2 = findViewById(R.id.textView2);
} //초기화작업을 생성자를 통해 하기위해 init method를 이용한다
public void setImage(int resId){
imageView.setImageResource(resId);
}
public void setTitle(String title){
textView.setText(title);
}
public void setComment(String comment){
textView2.setText(comment);
}
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

Layout01 layout01;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

layout01 = findViewById(R.id.layout01);
layout01.setImage(R.drawable.ic_launcher_foreground);
layout01.setTitle("hahahoho");
layout01.setComment("hihihihi");

Button btn = findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
layout01.setImage(R.drawable.icon);
}
});
Button btn2 = findViewById(R.id.btn2);
btn2.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
layout01.setImage(R.drawable.icon);
}
});
}
}

'android studio' 카테고리의 다른 글

xml을 이용한 focus event  (0) 2019.08.12
touch event, gesture event  (0) 2019.08.12
버튼 style변경(onDraw(), invalidate())  (0) 2019.08.12
toast message, style  (0) 2019.08.11
inflation  (0) 2019.08.11

onDraw()는 view가 화면에 보일때 자동으로 호출되는

method로 재정의를 통해 Button의 style을 지정할수있다

 

invalidate()는 onDraw() method를 호출하면서 

Button을 이동한 좌표에 지정된 style을 적용할수있다

 

activity_main.xml

<!--파일 위치를 불러온다-->
<com.example.myapplication.DemoButton
android:id="@+id/btn"
android:layout_width="250dp"
android:layout_height="100dp"
android:text="button style change"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

dimens.xml

//res\values\dimens.xml

<dimen name="text_size">10sp</dimen>
<!--getResources() method를 이용해 Resource 객채를 얻어오고
Resource 객체에 getDimension() method를 이용해 참조할수있다
setTextSize() method를 이용해 float pixel값으로 return받는다 -->

DemoButton.java

public class DemoButton extends AppCompatButton {

//소스코드에서 new연산자를 이용하여 생성하는경우 사용하는 생성자
public DemoButton(Context context){
super(context);

init(context);
} //생성자 생성
//안드로이드 UI 객체를 생성시에는
//반드시 Context를 인자로 사용한다

//view를 xml layout에 추가하는경우 사용하는 생성자
public DemoButton(Context context, AttributeSet attrs){
super(context, attrs);

init(context);
} //생성자 생성
//AttributeSet을통해 xml에 추가된 속성을 전달 받는다

//초기화를 위한 method
private void init(Context context){
setBackgroundColor(Color.CYAN);
setTextColor(Color.GREEN);
//text size 지정
float size = getResources().getDimension(R.dimen.text_size);
setTextSize(size);
}
//view가 화면에 그려질때 자동으로 호출되는 method
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.d("DemoButton", "onDraw()");
//onDraw호출되는 시점을 출력하기위해
}
//view가 터치될때 호출되는 method
public boolean onTouchEvent(MotionEvent event){
Log.d("DemoButton", "onTouchEvent()");
int action = event.getAction();

switch(action){
case MotionEvent.ACTION_DOWN:
setBackgroundColor(Color.GREEN);
setTextColor(Color.MAGENTA);
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
setBackgroundColor(Color.MAGENTA);
setTextColor(Color.GREEN);
break;
}
invalidate();
//설정된 값으로 다시 그려준다(onDraw() 호출)
return true;
}
}

 

'android studio' 카테고리의 다른 글

touch event, gesture event  (0) 2019.08.12
카드뷰(CardView)  (0) 2019.08.12
toast message, style  (0) 2019.08.11
inflation  (0) 2019.08.11
GridView  (0) 2019.08.11

+ Recent posts